https://wiki.freepascal.org/api.php?action=feedcontributions&user=Zoltanleo&feedformat=atomLazarus wiki - User contributions [en]2024-03-29T15:22:24ZUser contributionsMediaWiki 1.35.6https://wiki.freepascal.org/index.php?title=SQLDBRestBridge/ru&diff=157364SQLDBRestBridge/ru2023-09-25T20:43:38Z<p>Zoltanleo: /* Чего [компонент] не делает? */</p>
<hr />
<div>{{LanguageBar}}<br />
<br />
__TOC__<br />
== Цель ==<br />
<br />
Все больше и больше приложений перемещаются в Интернет: сегодня Pas2JS позволяет программировать паскаль для Интернета.<br />
<br />
Данные должны храниться на сервере, и REST сегодня является предпочтительной архитектурой для этого.<br />
Чаще всего данные попадают в базу данных.<br />
<br />
Поэтому необходим простой способ сделать данные доступными через службу REST.<br />
<br />
Модуль <tt>SQLDBRestBridge</tt> предлагает средство для раскрытия любой базы данных, к которой SQLDB может получить доступ, используя REST API.<br />
<br />
Это не подразумевается как общая структура REST API, для этого существуют другие платформы. Это также не структура RPC.<br />
<br />
Конструкция такова, что полная база данных может быть представлена с использованием одной строки кода с разумными настройками по умолчанию.<br />
<br />
<br />
=== Что компонент делает? ===<br />
<br />
По сути, этот компонент можно использовать в приложении FCL-Web для превращения его в REST-сервер:<br />
<br />
Затем сервер примет обычные команды REST GET/POST/PUT/Delete и автоматически переведет их в соответствующую базу данных CRUD (создание/чтение/обновление/удаление).<br />
<br />
Его можно использовать просто: одной строки кода достаточно, чтобы таким образом представить полную базу данных.<br />
<br />
Или это может быть полностью настроено и точно подкручено: схема может быть полностью определена: все операторы SQL могут быть заданы, могут быть определены списки полей, могут быть заданы псевдонимы, могут быть реализованы бизнес-правила.<br />
<br />
Полученный в результате сервер можно использовать в установке клиента/сервера (например, используя pas2js в качестве клиента) для обслуживания данных без необходимости написания всего необходимого сантехнического кода на REST-сервере.<br />
<br />
=== Чего компонент не делает? ===<br />
* Это технология на стороне сервера.<br />
: Чтобы использовать данные, обслуживаемые этим компонентом, вам нужно написать клиент.<br />
: Нет никаких предположений относительно клиента, за исключением того, что он знает, как выполнять HTTP-запросы, и что он понимает один из доступных форматов данных.<br />
* Он не предоставляет объекты в качестве ресурсов REST.<br />
: Используя некоторые приемы, это можно сделать, но это не цель проекта.<br />
: В конечном итоге все данные попадают в базу данных, и этот компонент использует прямой механизм для достижения этой цели.<br />
<br />
== Характеристики ==<br />
<br />
=== На стороне базы данных SQL ===<br />
<br />
# Каждый ресурс REST определяется до 4 операторов SQL, соответствующих 4 операциям CRUD.<br />
# Операторы CRUD могут быть автоматически сгенерированы на лету на основе имени таблицы и определений полей.<br />
# Для каждого поля можно указать псевдоним.<br />
# Доступны следующие типы на стороне клиента, автоматически сопоставленные с собственным типом базы данных:<br />
## String<br />
## 64-bit integer<br />
## 32-bit integer<br />
## Boolean<br />
## Date, Time, DateTime<br />
## Blob (base64-encoded)<br />
# Встроена поддержка последовательностей для генерации идентификаторов.<br />
# Полный контроль над тем, какие операции (GET, POST, PUT, DELETE) разрешены.<br />
# Ресурсы собраны в схему.<br />
# К сервису могут быть прикреплены несколько схем.<br />
# Могут быть заданы несколько баз данных (соединений).<br />
# Схема может быть привязана к одному соединению, или соединения могут совместно использовать схемы (вариант использования: другое соединение для клиента).<br />
# Компоненты бизнес-процессоров могут быть подключены к ресурсу, чтобы упростить реализацию бизнес-логики.<br />
# Выражения SQL могут содержать параметры, значения для параметров будут взяты из URL запроса.<br />
# Поддержка для получения пользовательских наборов данных.<br />
# Поддержка клиентских операторов SQL SELECT (необязательно, по умолчанию отключено)<br />
# Полная поддержка конфигурации через файл <tt>.ini</tt> из коробки.<br />
<br />
=== На стороне HTTP ===<br />
<br />
<ol><br />
<li>Аутентификация обрабатывается с использованием протокола HTTP.</li><br />
<li>Базовая аутентификация включена по умолчанию, но полностью подключаема.</li><br />
<li>Обычная аутентификация может искать действительных пользователей в базе данных (по умолчанию база данных незащищенная).</li><br />
<li>Выходной формат может быть зафиксирован или определен для каждого запроса (<code>?Fmt=format</code>). Обнаружение по типу контента также доступно.</li><br />
<li>Список полей для включения в вывод можно указать в URL:<br /><br />
<code>fl=field1,field2</code></li><br />
<li>Список полей, исключаемых из вывода, может быть указан в URL:<br /><br />
<code>fe=field1,field2</code></li><br />
<li>Различные форматы вывода доступны из коробки<br />
<ol><br />
<li>JSON (по умолчанию)</li><br />
<li>XML (пользовательский формат)</li><br />
<li>CSV (для ввода и вывода)</li><br />
<li>CDS (Формат, используемый Delphi'йским <code>TClientDataset</code>)</li><br />
<li>Запланировано: пакеты данных ADO (как он используется MS Access)</li></ol><br />
</li><br />
<li>Используется заводской шаблон, новые форматы могут быть добавлены по желанию.</li><br />
<li><p>Простые схемы URL. Доступны 2 основные схемы</p><br />
<pre class="text">BASEURL/Resource/<br />
BASEURL/Resource/ID</pre><br />
<p>или используя соединение в качестве префикса:</p><br />
<pre class="text">BASEURL/Connection/Resource/<br />
BASEURL/Connection/Resource/ID</pre></li><br />
<li><p>Поддержка самоанализа/обнаружения или метаданных:</p><br />
<pre class="text">BASEURL/metadata/</pre><br />
<p>возвращает список доступных ресурсов и их операций.</p><br />
<pre class="text">BASEURL/metadata/resourcename</pre><br />
возвращает структуру ресурса: поля, типы и т. д.</li><br />
<li><p>параметры <code>limit</code> и <code>offset</code> для подкачки результатов:</p><br />
<pre class="text">BASEURL/resourcename?limit=10<br />
BASEURL/resourcename?limit=10&amp;Offset=50</pre><br />
Может быть применен максимальный лимит. Когда операторы SQL поддерживают это, <tt>limit</tt> и <tt>offset</tt> переводятся в предложения fetch/offset [языка] SQL.</li><br />
<li><p>Порядок сортировки можно указать в URL-адресе запроса, если это позволяет определение ресурса, используя <code>?Sort=fieldname</code>. Возможность сортировки может быть указана на основе поля.</p><br />
<pre class="text">BASEURL/resourcename?sort=MyField<br />
BASEURL/resourcename?sort=MyField%20desc</pre></li><br />
<li><p>Фильтрация может быть указана на основе поля в URL-адресе запроса, и операция фильтрации преобразуется в SQL-предложение Where:</p><br />
<pre class="text">BASEURL/resourcename?MyField=10<br />
BASEURL/resourcename?MyField_null=1<br />
BASEURL/resourcename?MyField_null=0<br />
BASEURL/resourcename?MyField_lt=10<br />
BASEURL/resourcename?MyField_lte=10<br />
BASEURL/resourcename?MyField_gt=10<br />
BASEURL/resourcename?MyField_gte=10</pre><br />
<p>переводится в SQL-предложение WHERE:</p><br />
<pre>(MyField=10)<br />
(MyField is null)<br />
(MyField is not null)<br />
(MyField&lt;10)<br />
(MyField_lte&lt;=10)<br />
(MyField&gt;10)<br />
(MyField&gt;=10)</pre><br />
Возможность фильтрации по полю можно указать в схеме для каждого поля.</li><br />
<li><p>Запрос может указать, должны ли метаданные быть включены в ответ:</p><br />
<pre class="text">BASEURL/resourcename?metadata=1</pre></li><br />
<li><p>Запрос может указывать, должен ли результат быть понятным для человека или нет</p><br />
<pre class="text">BASEURL/resourcename?humanreadable=1</pre><br />
<p>Если установлено значение 1, результат запроса будет отформатирован. Не все форматы поддерживают это.</p></li></ol><br />
<br />
== Доступные компоненты ==<br />
<br />
=== TSQLDBRestSchema ===<br />
<br />
Представляет схему REST: список доступных ресурсов с их разрешенными операциями, определения полей, операторы SQL.<br />
<br />
==== Свойства ====<br />
<br />
* '''Resources''': коллекция ресурсов. Каждый пункт имеет тип ''TSQLDBRestResource''<br />
* '''ConnectionName''': установите это для имени соединения, если вы хотите, чтобы все ресурсы этой схемы использовали это соединение.<br />
<br />
==== Методы ====<br />
<br />
* '''SaveToFile''' сохраняет определения ресурса в файл (как JSON)<br />
<source lang="delphi">Procedure SaveToFile(Const aFileName : UTF8String);</source><br />
* '''SaveToFile''' сохраняет определения ресурсов в поток (как JSON)<br />
<source lang="delphi">Procedure SaveToStream(Const aStream : TStream);</source><br />
* '''AsJSON''' возвращает определения ресурса в виде объекта JSON<br />
<source lang="delphi">function AsJSON(const aPropName: UTF8String=''): TJSONData;</source><br />
Если указано, '''aPropName''' - это имя свойства, ниже которого хранятся все определения.<br />
* '''LoadFromFile''' загружает определения ресурсов из файла JSON<br />
<source lang="delphi">Procedure LoadFromFile(Const aFileName : UTF8String);</source><br />
* '''LoadFromStream''' загружает определения ресурсов из потока JSON<br />
<source lang="delphi">Procedure LoadFromStream(Const aStream : TStream);</source><br />
* '''FromJSON''' загружает определения ресурсов из файла JSON<br />
<source lang="delphi">Procedure FromJSON(aData: TJSONData;const aPropName: UTF8String='');</source><br />
Если указано, '''aPropName''' - имя свойства, под которым расположены все определения.<br />
* '''PopulateResourceFields''' создает поля ресурсов на основе имени таблицы<br />
<source lang="delphi">procedure PopulateResourceFields(aConn: TSQLConnection; aRes: TSQLDBRestResource; aMinFieldOpts : TRestFieldOptions = []);</source><br />
''PopulateResourceFields'' заполняет определения полей в ''aRes'', проверяя таблицу в соединении с базой данных ''aConn''. Каждое созданное поле получает как минимум ''aMinFieldOptions'' в его свойстве ''Options''<br />
* '''PopulateResources''' создает определение ресурса для каждой таблицы в соединении с базой данных<br />
<source lang="delphi">procedure PopulateResources(aConn: TSQLConnection; aTables: array of string; aMinFieldOpts: TRestFieldOptions= []);<br />
procedure PopulateResources(aConn : TSQLConnection; aTables : TStrings = Nil; aMinFieldOpts : TRestFieldOptions = []);</source><br />
* ''PopulateResources'' извлекает список таблиц из ''aConn'', создает определение ресурса для таблицы и вызывает ''PopulateResourceFields'', чтобы заполнить ресурс полями. Если указано ''aTables'', определения будут создаваться только для таблиц в этом списке.<br />
<br />
=== TSQLDBRestDispatcher ===<br />
<br />
==== Свойства ====<br />
<br />
* '''Active''' (Boolean) Регистрирует или отменяет регистрацию HTTP маршрутов<br />
* '''Connections''' (TSQLDBRestConnectionList) Список подключений к базе данных для соединения<br />
* '''Schemas''' (TSQLDBRestSchemaList) Список схем REST для обслуживания<br />
* '''BasePath''' (UTF8String) Базовый URL для REST URL<br />
* '''DefaultConnection''' (UTF8String) Соединение по умолчанию для использования, если ни одно не обнаружено из запроса/схемы<br />
* '''Strings''' (TRestStringsConfig Property) Конфигурации строк ввода/вывода<br />
* '''OutputOptions''' (TRestOutputOptions) Параметры вывода по умолчанию, изменяемые по запросу. Один или несколько из:<br />
<br />
# ''ooMetadata'' отправка метаданных в запросе<br />
# ''ooSparse'' не отправлять null-значения (если формат позволяет)<br />
# ''ooHumanReadable'' "человекочитабельная" печать<br />
<br />
* '''InputFormat''' (string) - Имя формата ввода, используемого для всех запросов.<br />
* '''OutputFormat''' (string) - Имя формата вывода, используемого для всех запросов.<br />
* '''DispatchOptions''' (TRestDispatcherOptions) - Опции диспетчера. Один или несколько из<br />
<br />
# ''rdoConnectionInURL'' - использовать соединение в качестве элемента пути в URL<br />
# ''rdoExposeMetadata'' - выставлять URL метаданных<br />
# ''rdoCustomView'' - разрешить использование пользовательского URL-адреса (выбирается SQL, указанный в запросе)<br />
# ''rdoHandleCORS'' - обрабатывать предпосылаемый запрос CORS<br />
# ''rdoAccessCheckNeedsDB'' - эта [опция] контролирует, когда проверка доступа выполняется для ресурсов: до или после установления соединения с базой данных. Если вам необходимо выполнить дополнительные запросы к базе данных, чтобы решить, предоставляется ли доступ (например, проверка ACL в БД), этот параметр должен быть включен<br />
<br />
* '''Authenticator''' (TRestAuthenticator) - аутентификатор для запросов. Если установлено значение ''OnBasicAuthentication'', его можно оставить пустым.<br />
* '''EnforceLimit''' (Integer) - если больше нуля, установить ограничение на выходные результаты.<br />
* '''CORSAllowedOrigins''' (String) - Разделенный запятыми список доменов, которым разрешено использовать эту службу REST. Если пусто, в предварительном запросе будет возвращено *.<br />
<br />
==== Методы ====<br />
<br />
<ul><br />
<li><p>'''ExposeDatabase''' выставляет базу данных в качестве службы REST. Это создаст определение соединения с предоставленными параметрами и вызовет '''ExposeConnection''' с этим определением.</p><br />
<source lang="delphi">Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : Array of String; aMinFieldOpt)<br />
Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : TStrings = nil; aMinFieldOpt)</source><br />
The connection gets the name '''ConnectionN''', where N is a unique number, and the schema is named '''SchemaConnectionN'''. The Tables and aMinFieldOpt arguments are the same as in the ''PopulateResources'' call of '''TSQLDBRestSchema'''</li><br />
<li><p>'''ExposeConnection''' exposes a database (represented by ''aConnection'') as a REST service. This will create a schema with a resource definition.</p><br />
<source lang="delphi">Function ExposeConnection(Const aConnection : TSQLDBRestConnection; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions</source><br />
<p>The Tables and aMinFieldOpt arguments are the same as in the ''PopulateResources'' call of '''TSQLDBRestSchema'''</p></li></ul><br />
<br />
=== TRestBasicAuthenticator ===<br />
This class implements HTTP Basic authentication for the SQLDB REST Dispatcher<br />
<br />
* AuthConnection (TSQLConnection) a SQL connection that will be used to run the SQL. By default the connection of the request is used. Set this if your users are in a separate database.<br />
* AuthenticateUserSQL (TStrings) an SQL Statement that will be executed on the connection (AuthConnection or connection of the request). This must contain 2 parameters '''userName''' and '''password''' and return a single field. This field will be used as '''UserID''' (available in the rest of the request flow.<br />
* DefaultUserName (UTF8String) a valid username, not in the database.<br />
* DefaultPassword (UTF8String) password of a default user.<br />
* DefaultUserID (UTF8String) the user ID reported if the default user is authenticated.<br />
* AuthenticationRealm : (UTF8String) Real sent in the '''WWW-Authenticate''' challenge to the client.<br />
* OnBasicAuthentication (event) triggered when basic authentication is requested. Here you can enter custom code to validate the user.<br />
<br />
=== TSQLDBRestModule ===<br />
<br />
* This is a simple '''TWebModule''' descendant which is usable in Lazarus IDE. It has a single ''Dispatcher'' property which must be set to a '''TSQLDBRestDispatcher''' instance.<br />
<br />
=== TSQLDBRestResource ===<br />
<br />
This is a collection item, found in the 'Resources' property of the '''TSQLDBRestSchema'''. It represents a single REST resource.<br />
==== properties ====<br />
<br />
It has the following properties:<br />
<br />
* '''Fields''' (TSQLDBRestFieldList) A list of fields that will be included in the output, or can be supplied in the input.<br />
* '''Enabled''' (Boolean) Is this resource enabled ?<br />
* '''InMetadata''' (Boolean) Should this resource be shown in the metadata list ?<br />
* '''ConnectionName''' (UTF8String) Connection name to use for this resource. <br />
* '''TableName''' (UTF8String) Database table name. This must only be specified if the SQL is auto generated.<br />
* '''ResourceName''' (UTF8String) Name of this resource as exposed to the outside world.<br />
* '''AllowedOperations''' (TRestOperations) The operations allowed on this resources (Get/Put/Post/Delete/Options/Head)<br />
* '''SQLSelect''' (TStrings) SQL select statement for a GET request. Left empty, it is autogenerated based on the table name. The where clause uses the fields with the ''foInKey'' flag set for single resources.<br />
* '''SQLInsert''' (TStrings) SQL INSERT statement for a POST request. Left empty, it is autogenerated based on the table name. <br />
* '''SQLUpdate''' (TStrings) SQL UPDATE statement for a PUT request. Left empty, it is autogenerated based on the table name. The where clause uses the fields with the ''foInKey'' flag set.<br />
* '''SQLDelete''' (TStrings) SQL DELETE statement for a DELETE request. Left empty, it is autogenerated based on the table name. The where clause uses the fields with the ''foInKey'' flag set.<br />
* '''SQL'' an array of SQL statements, based on the previous ones.<br />
* '''BusinessProcessor''' (TSQLDBRestCustomBusinessProcessor) at runtime this contains the business processor for this resource (if any)<br />
<br />
==== Events ====<br />
<br />
The following events are available:<br />
<br />
* '''OnResourceAllowed''' (TSQLDBRestAllowResourceEvent) Called to check whether the user is allowed to access this resource.<br />
* '''OnAllowedOperations''' (TSQLDBRestAllowedOperationsEvent) Called to allow to fine-tune the allowed operations for the user.<br />
* '''OnGetDataset''' (TSQLDBRestGetDatasetEvent) You can return a custom dataset for GET operations. <br />
* '''OnCheckParams''' (TSQLDBRestCheckParamsEvent) You can check the INSERT/UPDATE/DELETE SQL Statement parameters in this request/<br />
* '''OnAllowRecord''' (TSQLDBRestAllowRecordEvent) Called for each record, you can ddecide whether the record should be included in the output.<br />
<br />
Note: when set, the corresponding event in the business processor is not triggered.<br />
<br />
==== Methods ====<br />
<br />
The following methods exist:<br />
Function GetDataset(aContext : TBaseRestContext; aFieldList : TRestFieldPairArray; aOrderBy : TRestFieldOrderPairArray; aLimit, aOffset : Int64) : TDataset;<br />
Get the custom dataset for this resource.<br />
function GenerateDefaultSQL(aKind: TSQLKind): UTF8String; virtual;<br />
Get the default SQL for this resource if none was specified.<br />
function GetFieldList(aListKind: TFieldListKind): UTF8String;<br />
Return a list of fields as a string, separated by a correct separator token <br />
function GetFieldArray(aListKind: TFieldListKind): TSQLDBRestFieldArray;<br />
Return a list of fields, similar to GetFieldList, but returns the raw fields.<br />
Function GetResolvedSQl(aKind : TSQLKind; Const AWhere : UTF8String; Const aOrderBy : UTF8String = ''; aLimit : UTF8String = '') : UTF8String;<br />
Return the actual SQL Statement that will be executed, with macros <br />
Procedure PopulateFieldsFromFieldDefs(Defs : TFieldDefs; aIndexFields : TStringArray; aProcessIdentifier : TProcessIdentifier; aMinFieldOpts : TRestFieldOptions);<br />
Populate the fields collection from a TFieldDefs collection.<br />
<br />
=== TSQLDBRestField ===<br />
The TSQLDBREstResource has a collection of fields (TSQLDBRestField) that determine the output fields of the REST resource<br />
<br />
It has the following properties;<br />
* FieldName (UTF8String Read FFieldName Write FFieldName;<br />
* PublicName (UTF8String Read GetPublicName Write FPublicName;<br />
* GeneratorName (String) name of a generator to privide a unique value.<br />
* FieldType (TRestFieldType) Data type of the exposed field.One of (the names speak for themselves)<br />
** rftInteger<br />
** rftLargeInt<br />
** rftFloat<br />
** rftDate<br />
** rftTime<br />
** rftDateTime<br />
** rftString<br />
** rftBoolean<br />
** rftBlob (sent as base64-encoded data)<br />
* NativeFieldType (TFieldType) Native database field type of this field.<br />
* Options (TRestFieldOptions) options for this field. One of more of:<br />
** foInKey This is a key field.<br />
** foInInsert The field can be inserted (if statement is autogenerated)<br />
** foInUpdate The field can be updated (if statement is autogenerated) <br />
** foRequired A field value is required.<br />
** foFilter Filtering is allowed on this field.<br />
** foOrderBy Ordering is allowed on this field. <br />
** foOrderByDesc Ordering (descendent) is allowed on this field. <br />
* Filters (TRestFieldFilters) Allowed filter expressions for this field. One or more of:<br />
** rfEqual<br />
** rfLessThan<br />
** rfGreaterThan<br />
** rfLessThanEqual<br />
** rfGreaterThanEqual<br />
** rfNull<br />
* MaxLen (integer) for string fields, the maximum allowed length of the field.<br />
<br />
=== TSQLDBRestBusinessProcessor ===<br />
This class serves to implement any business rules you want to attach to your rest resource.<br />
You can drop one TSQLDBRestBusinessProcessor instance per resource on a module, set the <br />
schema and resource name (a list of resource names is available in the Object Inspector) and implement the events.<br />
<br />
The following properties and events are available:<br />
* '''Schema''' : The schema in which the resource resides.<br />
* '''ResourceName''' The resource for which the business rules are valid.<br />
* '''OnGetDataset''' : Event called when you want to create a custom dataset. You must free the dataset yourself.<br />
* '''OnCheckParams''' : Event in which you can verify whether the parameters for insert/update/delete/select queries are OK.<br />
* '''OnAllowResource''' : Event to decide whether a property is allowed for a request. You can use this e.g. to forbid certain users from accessing a resource.<br />
* '''OnAllowedOperations''' : Event to decide whether a REST operation is allowed for a request. You can use this to forbid certain users from writing to a resource, but allow them to read.<br />
* '''OnAllowRecord''' : Called for each record which will be output. This can be used to prevent certain records from being streamed to the output. Note that if ''Limit'' is specified, this will not be taken into account, i.e. if the limit is 10, and you forbid 2 records from being streamed at this point, only 8 records will be returned.<br />
<br />
All calls get passed a TRestContext instance. This contains 2 properties and a method:<br />
Function GetVariable(Const aName : UTF8String; aSources : TVariableSources; Out aValue : UTF8String) : Boolean;<br />
Call this to get a HTTP Query variable, header,... The function returns true if the variable was found.<br />
Property UserID : UTF8String<br />
This will be set when calling.<br />
Property Data : TObject<br />
You can attach data to this if you want to. It will be kept for the duration of the request.<br />
You are responsible for freeing this data, though.<br />
<br />
== Special Resources ==<br />
<br />
=== metaData ===<br />
<br />
The ''/baseURL/metaData'' resource lists all the resources defined in the schema, together with the allowed operations.<br />
<br />
The usual formats and parameters are supported.<br />
<br />
=== metaData/resourceName ===<br />
<br />
The ''/baseURL/metaData/resourceName'' resource lists all the fields in the '''resourceName''' resource, together with the properties.<br />
<br />
The usual formats and parameters are supported for this resource.<br />
<br />
<br />
=== customview ===<br />
<br />
The customView resource is special. Its availability must be enabled using the ''Dispatcher'''s Options. When ''rdoCustomView'' is included in the options, the <br />
<br />
baseURL/customView<br />
<br />
URL becomes available. It allows the client to specify an SQL Select statement using the ''SQL'' parameter:<br />
<br />
baseURL/customView?SQL=select count(*) as thecount from person<br />
<br />
(you must encode the sql using the usual URI encoding mechanism, it is omitted for readability reasons here)<br />
The usual parameters for fieldlists, format are supported, but not the ''limit'' and ''offset'' parameters.<br />
<br />
The engine will check that only SELECT statements passed on. <br />
Nevertheless, this is an inherently less safe mechanism which should only be used in case of need.<br />
<br />
=== _connection ===<br />
<br />
(Work in progress)<br />
This is a resource that allows you to manage the connections through REST. <br />
This can be useful in development environments, where you can simply create a single binary that does not need any configuration, <br />
the whole configuration can be done through REST, including the management of the available connections.<br />
<br />
Because this is security-wise a dangerous option to enable, it is disabled by default, and must be enabled explicitly with the ''rdoConnectionResource'' option of the dispatcher.<br />
<br />
Use cases are a development environment where this can be used to confiure the restserver from within the IDE. <br />
When moving to production, the option can be disabled, and only fixed and known connections will be allowed/<br />
<br />
== Available Output formats ==<br />
<br />
=== JSON (the default) ===<br />
<br />
* A simple JSON object encapsulating data, metadata and possibly error nodes.<br />
* rows are exported as JSON objects, field names are property names of the object.<br />
* Name: 'json'<br />
* Content-type: 'application/json'<br />
* Input and output.<br />
* Data by default under 'data'<br />
* Metadata by default under 'metaData'<br />
* Errors reported under 'error'<br />
* Property names are configurable in REST dispatcher.<br />
<br />
=== XML (a custom format) ===<br />
<br />
* A simple XML document encapsulating data, metadata and possibly error nodes, under a root element 'datapacket'<br />
* rows are exported as XML elements (named 'row') , the contents as text in the element.<br />
* Name: 'xml'<br />
* Content-type: 'text/xml'<br />
* Input and output.<br />
* Data by default under 'data'<br />
* Metadata by default under 'metadata'<br />
* Errors reported under 'error'<br />
* Element names are configurable in REST dispatcher.<br />
<br />
=== CSV ===<br />
<br />
* Simple comma-separated CSV list.<br />
* Name: 'csv'<br />
* Content-type: 'text/csv'<br />
* Input and output.<br />
* Separator: Comma<br />
* Quotes when needed.<br />
* Metadata means first line contains fieldnames.<br />
<br />
=== CDS ===<br />
<br />
* XML package using the XMLFormat used by Delphi <code>TClientDataset</code>.<br />
* Name: 'cds'<br />
* Content-type: 'text/csv'<br />
* Input and output.<br />
<br />
A Delphi demo program is provided.<br />
<br />
=== ADO ===<br />
<br />
* XML format as used by ADO recordsets (as used by MS Access)<br />
* Name: 'ado'<br />
* Input and output.<br />
<br />
== Ini file Support ==<br />
<br />
The following classes can load/save their configuration settings from/to an .ini file<br />
<br />
* TSQLDBRestDispatcher<br />
* TRestBasicAuthenticator<br />
* TSQLDBRestConnection<br />
* TRestStringsConfig <br />
<br />
The support for this is implemented in 2 units;<br />
* the sqldbauthini unit has a type helper for the TRestBasicAuthenticator class. <br />
* The sqldbrestini unit has the type helpers for TSQLDBRestDispatcher and TSQLDBRestConnection and TRestStringsConfig. <br />
<br />
The helpers implement LoadFromFile/SaveToFile methods, as well as LoadFromIni/SaveToIni methods.<br />
There are some options to control the level of depth.<br />
<br />
The schemas (if so required) are saved as JSON files in the same directory as the .ini file, using the name of the schema as the filename.<br />
<br />
== Lazarus Support ==<br />
<br />
Lazarus support for the SQLDB REST Bridge is in the lazsqldbrest.lpk package, under the fpweb directory. <br />
It registers the TSQLDBRestDispatcher, TSQLDBRestSchema and TRestBasicAuthenticator components on the component palette:<br />
<br />
[[File:sqldbrestcomponents.png]]<br />
<br />
Additionally it registers some component- and property editors:<br />
<br />
Further integration with Lazarus is planned:<br />
1. Add a nicer schema editor<br />
1. Let the IDE act as a REST server. (alternatively, add a tool with a menu entry under tools for easy configuration)<br />
<br />
Lazarus support can work with FPC 3.0.4. <br />
To make this work, the files can be copied from SVN or a 3.2 release to the lazarus fpweb directory. <br />
<br />
To this end, <br />
# create a directory '''src''' below the '''components/fpweb directory''' (where the package is located) <br />
# copy all files in directory<br />
packages/fcl-web/src/restbridge de<br />
: to directory<br />
lazarus/components/fpweb/src<br />
# compile the lazsqldbrest.lpk package. It should find the files in the src directory and use them.<br />
<br />
== Pas2JS Support ==<br />
The SQLDB Rest bridge will be integrated in the compile server that comes with pas2JS:<br />
This means that pas2js will come with a toll that<br />
* Can recompile your web project on the fly<br />
* Can serve files on disc<br />
* Can act as a REST server.<br />
So no actual server is needed during development.<br />
<br />
== Usage ==<br />
<br />
=== In a fcl-web application. ===<br />
<br />
The rest bridge was designed to be simple to use.<br />
<br />
==== Expose a single database, no authentication. ====<br />
<br />
Simple expose of a database. Run the following code in the startup of your program (for example, DoRun of the web application class, or startup code of the program)<br />
<br />
<source lang="delphi">FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.ExposeDatabase(<br />
'postgres','localhost','expensetracker','me','secret'<br />
,Nil,<br />
[foFilter,foInInsert,foInUpdate,foOrderByDesc]);<br />
FDisp.Active:=True; <br />
FDisp.SaveToFile('demo.ini');</source><br />
This exposes all tables of the database 'expenstracker', allows to filter,update and sort on all fields. The connection will be named 'Connection1', the schema 'SchemaConnection1'<br />
<br />
The configuration will be saved in a file called 'demo.ini'.<br /><br />
It can be reused to quickly set up a REST dispatcher.<br />
<br />
==== Expose single database, HTTP Basic authentication. ====<br />
<br />
Similar to the previous example, but with HTTP BASIC authentication for a single user:<br />
<br />
<source lang="delphi">FAuth:=TRestBasicAuthenticator.Create(Self);<br />
FAuth.DefaultUserName:='me';<br />
FAuth.DefaultPassword:='secret';<br />
FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.Authenticator:=Fauth;<br />
FDisp.ExposeDatabase(<br />
'postgres','localhost','expensetracker','me','secret'<br />
,Nil,<br />
[foFilter]);<br />
FDisp.Active:=True; </source><br />
This exposes all tables of the database 'expenstracker', allows to filter,update and sort on all fields. The connection will be named 'Connection1', the schema 'SchemaConnection1'. No updates are possible: the fields will not be in update/insert statements. Sorting is also not allowed.<br />
<br />
==== Expose single database, HTTP Basic authentication (2) ====<br />
<br />
Smilar to the previous example, but with HTTP BASIC authentication, using the database to authenticate a user:<br />
<br />
<source lang="delphi">FAuth:=TRestBasicAuthenticator.Create(Self);<br />
FAuth.AuthenticateUserSQL.Text:='select uID from users where (uLogin=:UserName) and (uPassword=:Password)';<br />
FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.Authenticator:=Fauth;<br />
FDisp.ExposeDatabase(<br />
'postgres','localhost','expensetracker','me','secret'<br />
,['projects','expenses'],<br />
[]);<br />
FDisp.Active:=True; </source><br />
Here only the 'projects' and 'expenses' tables are exposed, read-only.<br />
<br />
==== Using a Config file and schema files. ====<br />
<br />
Start a dispatcher, loading the file demo.ini. The dioSkipReadSchemas tells it not to load schema files, the schemas will be created from the connections.<br />
<br />
<source lang="delphi">FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.LoadFromFile('demo.ini',[dioSkipReadSchemas]);<br />
FDisp.Active:=True;</source><br />
The .ini file can be created using SaveToFile on a configured dispatcher instance.<br />
<br />
=== In the lazarus IDE. ===<br />
<br />
To work in the lazarus IDE with the SQLDB Rest bridge, you must install the '''lazsqldbrest''' package, which can be found in the directory components/fpWeb.<br />
<br />
<br />
==== Using a datamodule ====<br />
<br />
In this approach, a datamodule is created that is in memory throughout the whole lifetime of the application (fcl-web application)<br />
<br />
<br />
# Create a HTTP server application or FCGI application. (a CGI will also work but is very inefficient)<br />
# Add a plain datamodule to the application.<br />
# Drop a TSQLDBRestSchema class from the 'FCL-Web' tab of the component palette<br />
# Edit the schema (property resources), or import it from file using the component editor context menu<br />
# Drop a TSQLDBRestDispatcher class from the 'FCL-Web' tab of the component palette<br />
# Edit the connections<br />
# Add an item to the Schemas property and point it to the TSQLDBRestSchema added previously <br />
# For HTTP Basic authentication, add a TRESTBAsicAuthenticator class, and set the TSQLDBRestDispatcher instance's Authentocator property to it. <br />
<br />
The end result should look more or less like this:<br />
<br />
[[File:restmodule.png]]<br />
<br />
That's it. The project is ready to run.<br />
<br />
==== Using a SQLDBRestdatamodule ====<br />
<br />
This approach is basically the same as the previous one.<br />
<br />
# Create a HTTP application<br />
# Under File - New, create a SQLDB Rest Bridge Module.<br />
# Drop a SQLDBRestDispatcher component on the module.<br />
# The Dispatcher property of the module should be set to the SQLDBRestDispatcher component.<br />
# Set up the component as required, as in the previous example.<br />
<br />
== Examples ==<br />
<br />
=== FPC ===<br />
* in FPC Packages, the fcl-web/examples/restbridge directory, there is a small example that serves a expenses tracker database.<br />
* The directory (delphiclient) below contains a small Delphi project that shows how to connect a TClientDataset to the SQLDB Rest bridge.<br />
: Design time view:<br />
: [[File:delphirestclientdesign.png]]<br />
: Run time view:<br />
: [[File:delphirestclient.png]]<br />
<br />
=== Lazarus ===<br />
There are 2 server examples available, and several client examples.<br />
The client examples are all the same, they just use a different kind of ''TDataset'' to show the data coming from the server: the '''buf''', '''json''' and '''CSV''' formats are demonstrated in this way.<br />
==== Server examples ====<br />
<br />
* The demo/restbridge directory of Lazarus/Components contains the same example program, using a datamodule.<br />
* The demo/restmodule directory of Lazarus/Components contains the same example program, using a SQLDB Rest Web datamodule.<br />
<br />
==== TBufDataset client example ====<br />
* The demo/bufclient directory contains a demo that shows how to load data from a SQLDB REST bridge server in a TBUFDataset dataset. It uses the 'buf' format to do so.<br />
: [[File:sqldbrestbufclientdata.png]]<br />
<br />
: [[File:sqldbrestbufclientraw.png]]<br />
: it also demonstrates how to use the metadata resource of the server to list the available resources<br />
==== TJSONDataset client example ====<br />
<br />
* The demo/jsonclient directory contains a demo that is similar to the bufclient demo: <br />
: it shows how to load data from a SQLDB REST bridge server in a TBaseJSONDataset dataset, using the 'csv' format<br />
: [[File:sqldbrestjsonclientdata.png]]<br />
<br />
: [[File:sqldbrestjsonclientraw.png]]<br />
: Like the bufdataset demo, it also demonstrates how to use the metadata resource of the server to list the available resources<br />
<br />
==== TCSVDataset client example ====<br />
* The demo/csvclient directory contains a demo that is similar to the bufclient demo: <br />
: it shows how to load data from a SQLDB REST bridge server in a TCSVDataset dataset, using the 'csv' format<br />
<br />
=== Pas2JS ===<br />
* A similar application to the lazarus bufflien/jsonclient programs exists for pas2JS in demo/restbridge/simple<br />
: [[File:sqldbrestbridgepas2js.png]]<br />
<br />
== TODO ==<br />
<br />
The following extensions are still planned:<br />
<br />
* Support for <code>?q=filterexpression</code> in URL filters.<br />
* Connection management API. (zero-config service)<br />
* Use HTTP credentials to connect to the database.<br />
* Unknown query params are now ignored. Allow to check for valid query parameters and raise error if unknown query param is encountered.<br />
<br />
<br />
[[Category:Databases/ru]]<br />
{{AutoCategory}}</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=SQLDBRestBridge/ru&diff=157363SQLDBRestBridge/ru2023-09-25T20:43:08Z<p>Zoltanleo: /* Что [компонент] делает? */</p>
<hr />
<div>{{LanguageBar}}<br />
<br />
__TOC__<br />
== Цель ==<br />
<br />
Все больше и больше приложений перемещаются в Интернет: сегодня Pas2JS позволяет программировать паскаль для Интернета.<br />
<br />
Данные должны храниться на сервере, и REST сегодня является предпочтительной архитектурой для этого.<br />
Чаще всего данные попадают в базу данных.<br />
<br />
Поэтому необходим простой способ сделать данные доступными через службу REST.<br />
<br />
Модуль <tt>SQLDBRestBridge</tt> предлагает средство для раскрытия любой базы данных, к которой SQLDB может получить доступ, используя REST API.<br />
<br />
Это не подразумевается как общая структура REST API, для этого существуют другие платформы. Это также не структура RPC.<br />
<br />
Конструкция такова, что полная база данных может быть представлена с использованием одной строки кода с разумными настройками по умолчанию.<br />
<br />
<br />
=== Что компонент делает? ===<br />
<br />
По сути, этот компонент можно использовать в приложении FCL-Web для превращения его в REST-сервер:<br />
<br />
Затем сервер примет обычные команды REST GET/POST/PUT/Delete и автоматически переведет их в соответствующую базу данных CRUD (создание/чтение/обновление/удаление).<br />
<br />
Его можно использовать просто: одной строки кода достаточно, чтобы таким образом представить полную базу данных.<br />
<br />
Или это может быть полностью настроено и точно подкручено: схема может быть полностью определена: все операторы SQL могут быть заданы, могут быть определены списки полей, могут быть заданы псевдонимы, могут быть реализованы бизнес-правила.<br />
<br />
Полученный в результате сервер можно использовать в установке клиента/сервера (например, используя pas2js в качестве клиента) для обслуживания данных без необходимости написания всего необходимого сантехнического кода на REST-сервере.<br />
<br />
=== Чего [компонент] не делает? ===<br />
* Это технология на стороне сервера.<br />
: Чтобы использовать данные, обслуживаемые этим компонентом, вам нужно написать клиент.<br />
: Нет никаких предположений относительно клиента, за исключением того, что он знает, как выполнять HTTP-запросы, и что он понимает один из доступных форматов данных.<br />
* Он не предоставляет объекты в качестве ресурсов REST.<br />
: Используя некоторые приемы, это можно сделать, но это не цель проекта.<br />
: В конечном итоге все данные попадают в базу данных, и этот компонент использует прямой механизм для достижения этой цели.<br />
<br />
== Характеристики ==<br />
<br />
=== На стороне базы данных SQL ===<br />
<br />
# Каждый ресурс REST определяется до 4 операторов SQL, соответствующих 4 операциям CRUD.<br />
# Операторы CRUD могут быть автоматически сгенерированы на лету на основе имени таблицы и определений полей.<br />
# Для каждого поля можно указать псевдоним.<br />
# Доступны следующие типы на стороне клиента, автоматически сопоставленные с собственным типом базы данных:<br />
## String<br />
## 64-bit integer<br />
## 32-bit integer<br />
## Boolean<br />
## Date, Time, DateTime<br />
## Blob (base64-encoded)<br />
# Встроена поддержка последовательностей для генерации идентификаторов.<br />
# Полный контроль над тем, какие операции (GET, POST, PUT, DELETE) разрешены.<br />
# Ресурсы собраны в схему.<br />
# К сервису могут быть прикреплены несколько схем.<br />
# Могут быть заданы несколько баз данных (соединений).<br />
# Схема может быть привязана к одному соединению, или соединения могут совместно использовать схемы (вариант использования: другое соединение для клиента).<br />
# Компоненты бизнес-процессоров могут быть подключены к ресурсу, чтобы упростить реализацию бизнес-логики.<br />
# Выражения SQL могут содержать параметры, значения для параметров будут взяты из URL запроса.<br />
# Поддержка для получения пользовательских наборов данных.<br />
# Поддержка клиентских операторов SQL SELECT (необязательно, по умолчанию отключено)<br />
# Полная поддержка конфигурации через файл <tt>.ini</tt> из коробки.<br />
<br />
=== На стороне HTTP ===<br />
<br />
<ol><br />
<li>Аутентификация обрабатывается с использованием протокола HTTP.</li><br />
<li>Базовая аутентификация включена по умолчанию, но полностью подключаема.</li><br />
<li>Обычная аутентификация может искать действительных пользователей в базе данных (по умолчанию база данных незащищенная).</li><br />
<li>Выходной формат может быть зафиксирован или определен для каждого запроса (<code>?Fmt=format</code>). Обнаружение по типу контента также доступно.</li><br />
<li>Список полей для включения в вывод можно указать в URL:<br /><br />
<code>fl=field1,field2</code></li><br />
<li>Список полей, исключаемых из вывода, может быть указан в URL:<br /><br />
<code>fe=field1,field2</code></li><br />
<li>Различные форматы вывода доступны из коробки<br />
<ol><br />
<li>JSON (по умолчанию)</li><br />
<li>XML (пользовательский формат)</li><br />
<li>CSV (для ввода и вывода)</li><br />
<li>CDS (Формат, используемый Delphi'йским <code>TClientDataset</code>)</li><br />
<li>Запланировано: пакеты данных ADO (как он используется MS Access)</li></ol><br />
</li><br />
<li>Используется заводской шаблон, новые форматы могут быть добавлены по желанию.</li><br />
<li><p>Простые схемы URL. Доступны 2 основные схемы</p><br />
<pre class="text">BASEURL/Resource/<br />
BASEURL/Resource/ID</pre><br />
<p>или используя соединение в качестве префикса:</p><br />
<pre class="text">BASEURL/Connection/Resource/<br />
BASEURL/Connection/Resource/ID</pre></li><br />
<li><p>Поддержка самоанализа/обнаружения или метаданных:</p><br />
<pre class="text">BASEURL/metadata/</pre><br />
<p>возвращает список доступных ресурсов и их операций.</p><br />
<pre class="text">BASEURL/metadata/resourcename</pre><br />
возвращает структуру ресурса: поля, типы и т. д.</li><br />
<li><p>параметры <code>limit</code> и <code>offset</code> для подкачки результатов:</p><br />
<pre class="text">BASEURL/resourcename?limit=10<br />
BASEURL/resourcename?limit=10&amp;Offset=50</pre><br />
Может быть применен максимальный лимит. Когда операторы SQL поддерживают это, <tt>limit</tt> и <tt>offset</tt> переводятся в предложения fetch/offset [языка] SQL.</li><br />
<li><p>Порядок сортировки можно указать в URL-адресе запроса, если это позволяет определение ресурса, используя <code>?Sort=fieldname</code>. Возможность сортировки может быть указана на основе поля.</p><br />
<pre class="text">BASEURL/resourcename?sort=MyField<br />
BASEURL/resourcename?sort=MyField%20desc</pre></li><br />
<li><p>Фильтрация может быть указана на основе поля в URL-адресе запроса, и операция фильтрации преобразуется в SQL-предложение Where:</p><br />
<pre class="text">BASEURL/resourcename?MyField=10<br />
BASEURL/resourcename?MyField_null=1<br />
BASEURL/resourcename?MyField_null=0<br />
BASEURL/resourcename?MyField_lt=10<br />
BASEURL/resourcename?MyField_lte=10<br />
BASEURL/resourcename?MyField_gt=10<br />
BASEURL/resourcename?MyField_gte=10</pre><br />
<p>переводится в SQL-предложение WHERE:</p><br />
<pre>(MyField=10)<br />
(MyField is null)<br />
(MyField is not null)<br />
(MyField&lt;10)<br />
(MyField_lte&lt;=10)<br />
(MyField&gt;10)<br />
(MyField&gt;=10)</pre><br />
Возможность фильтрации по полю можно указать в схеме для каждого поля.</li><br />
<li><p>Запрос может указать, должны ли метаданные быть включены в ответ:</p><br />
<pre class="text">BASEURL/resourcename?metadata=1</pre></li><br />
<li><p>Запрос может указывать, должен ли результат быть понятным для человека или нет</p><br />
<pre class="text">BASEURL/resourcename?humanreadable=1</pre><br />
<p>Если установлено значение 1, результат запроса будет отформатирован. Не все форматы поддерживают это.</p></li></ol><br />
<br />
== Доступные компоненты ==<br />
<br />
=== TSQLDBRestSchema ===<br />
<br />
Представляет схему REST: список доступных ресурсов с их разрешенными операциями, определения полей, операторы SQL.<br />
<br />
==== Свойства ====<br />
<br />
* '''Resources''': коллекция ресурсов. Каждый пункт имеет тип ''TSQLDBRestResource''<br />
* '''ConnectionName''': установите это для имени соединения, если вы хотите, чтобы все ресурсы этой схемы использовали это соединение.<br />
<br />
==== Методы ====<br />
<br />
* '''SaveToFile''' сохраняет определения ресурса в файл (как JSON)<br />
<source lang="delphi">Procedure SaveToFile(Const aFileName : UTF8String);</source><br />
* '''SaveToFile''' сохраняет определения ресурсов в поток (как JSON)<br />
<source lang="delphi">Procedure SaveToStream(Const aStream : TStream);</source><br />
* '''AsJSON''' возвращает определения ресурса в виде объекта JSON<br />
<source lang="delphi">function AsJSON(const aPropName: UTF8String=''): TJSONData;</source><br />
Если указано, '''aPropName''' - это имя свойства, ниже которого хранятся все определения.<br />
* '''LoadFromFile''' загружает определения ресурсов из файла JSON<br />
<source lang="delphi">Procedure LoadFromFile(Const aFileName : UTF8String);</source><br />
* '''LoadFromStream''' загружает определения ресурсов из потока JSON<br />
<source lang="delphi">Procedure LoadFromStream(Const aStream : TStream);</source><br />
* '''FromJSON''' загружает определения ресурсов из файла JSON<br />
<source lang="delphi">Procedure FromJSON(aData: TJSONData;const aPropName: UTF8String='');</source><br />
Если указано, '''aPropName''' - имя свойства, под которым расположены все определения.<br />
* '''PopulateResourceFields''' создает поля ресурсов на основе имени таблицы<br />
<source lang="delphi">procedure PopulateResourceFields(aConn: TSQLConnection; aRes: TSQLDBRestResource; aMinFieldOpts : TRestFieldOptions = []);</source><br />
''PopulateResourceFields'' заполняет определения полей в ''aRes'', проверяя таблицу в соединении с базой данных ''aConn''. Каждое созданное поле получает как минимум ''aMinFieldOptions'' в его свойстве ''Options''<br />
* '''PopulateResources''' создает определение ресурса для каждой таблицы в соединении с базой данных<br />
<source lang="delphi">procedure PopulateResources(aConn: TSQLConnection; aTables: array of string; aMinFieldOpts: TRestFieldOptions= []);<br />
procedure PopulateResources(aConn : TSQLConnection; aTables : TStrings = Nil; aMinFieldOpts : TRestFieldOptions = []);</source><br />
* ''PopulateResources'' извлекает список таблиц из ''aConn'', создает определение ресурса для таблицы и вызывает ''PopulateResourceFields'', чтобы заполнить ресурс полями. Если указано ''aTables'', определения будут создаваться только для таблиц в этом списке.<br />
<br />
=== TSQLDBRestDispatcher ===<br />
<br />
==== Свойства ====<br />
<br />
* '''Active''' (Boolean) Регистрирует или отменяет регистрацию HTTP маршрутов<br />
* '''Connections''' (TSQLDBRestConnectionList) Список подключений к базе данных для соединения<br />
* '''Schemas''' (TSQLDBRestSchemaList) Список схем REST для обслуживания<br />
* '''BasePath''' (UTF8String) Базовый URL для REST URL<br />
* '''DefaultConnection''' (UTF8String) Соединение по умолчанию для использования, если ни одно не обнаружено из запроса/схемы<br />
* '''Strings''' (TRestStringsConfig Property) Конфигурации строк ввода/вывода<br />
* '''OutputOptions''' (TRestOutputOptions) Параметры вывода по умолчанию, изменяемые по запросу. Один или несколько из:<br />
<br />
# ''ooMetadata'' отправка метаданных в запросе<br />
# ''ooSparse'' не отправлять null-значения (если формат позволяет)<br />
# ''ooHumanReadable'' "человекочитабельная" печать<br />
<br />
* '''InputFormat''' (string) - Имя формата ввода, используемого для всех запросов.<br />
* '''OutputFormat''' (string) - Имя формата вывода, используемого для всех запросов.<br />
* '''DispatchOptions''' (TRestDispatcherOptions) - Опции диспетчера. Один или несколько из<br />
<br />
# ''rdoConnectionInURL'' - использовать соединение в качестве элемента пути в URL<br />
# ''rdoExposeMetadata'' - выставлять URL метаданных<br />
# ''rdoCustomView'' - разрешить использование пользовательского URL-адреса (выбирается SQL, указанный в запросе)<br />
# ''rdoHandleCORS'' - обрабатывать предпосылаемый запрос CORS<br />
# ''rdoAccessCheckNeedsDB'' - эта [опция] контролирует, когда проверка доступа выполняется для ресурсов: до или после установления соединения с базой данных. Если вам необходимо выполнить дополнительные запросы к базе данных, чтобы решить, предоставляется ли доступ (например, проверка ACL в БД), этот параметр должен быть включен<br />
<br />
* '''Authenticator''' (TRestAuthenticator) - аутентификатор для запросов. Если установлено значение ''OnBasicAuthentication'', его можно оставить пустым.<br />
* '''EnforceLimit''' (Integer) - если больше нуля, установить ограничение на выходные результаты.<br />
* '''CORSAllowedOrigins''' (String) - Разделенный запятыми список доменов, которым разрешено использовать эту службу REST. Если пусто, в предварительном запросе будет возвращено *.<br />
<br />
==== Методы ====<br />
<br />
<ul><br />
<li><p>'''ExposeDatabase''' выставляет базу данных в качестве службы REST. Это создаст определение соединения с предоставленными параметрами и вызовет '''ExposeConnection''' с этим определением.</p><br />
<source lang="delphi">Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : Array of String; aMinFieldOpt)<br />
Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : TStrings = nil; aMinFieldOpt)</source><br />
The connection gets the name '''ConnectionN''', where N is a unique number, and the schema is named '''SchemaConnectionN'''. The Tables and aMinFieldOpt arguments are the same as in the ''PopulateResources'' call of '''TSQLDBRestSchema'''</li><br />
<li><p>'''ExposeConnection''' exposes a database (represented by ''aConnection'') as a REST service. This will create a schema with a resource definition.</p><br />
<source lang="delphi">Function ExposeConnection(Const aConnection : TSQLDBRestConnection; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions</source><br />
<p>The Tables and aMinFieldOpt arguments are the same as in the ''PopulateResources'' call of '''TSQLDBRestSchema'''</p></li></ul><br />
<br />
=== TRestBasicAuthenticator ===<br />
This class implements HTTP Basic authentication for the SQLDB REST Dispatcher<br />
<br />
* AuthConnection (TSQLConnection) a SQL connection that will be used to run the SQL. By default the connection of the request is used. Set this if your users are in a separate database.<br />
* AuthenticateUserSQL (TStrings) an SQL Statement that will be executed on the connection (AuthConnection or connection of the request). This must contain 2 parameters '''userName''' and '''password''' and return a single field. This field will be used as '''UserID''' (available in the rest of the request flow.<br />
* DefaultUserName (UTF8String) a valid username, not in the database.<br />
* DefaultPassword (UTF8String) password of a default user.<br />
* DefaultUserID (UTF8String) the user ID reported if the default user is authenticated.<br />
* AuthenticationRealm : (UTF8String) Real sent in the '''WWW-Authenticate''' challenge to the client.<br />
* OnBasicAuthentication (event) triggered when basic authentication is requested. Here you can enter custom code to validate the user.<br />
<br />
=== TSQLDBRestModule ===<br />
<br />
* This is a simple '''TWebModule''' descendant which is usable in Lazarus IDE. It has a single ''Dispatcher'' property which must be set to a '''TSQLDBRestDispatcher''' instance.<br />
<br />
=== TSQLDBRestResource ===<br />
<br />
This is a collection item, found in the 'Resources' property of the '''TSQLDBRestSchema'''. It represents a single REST resource.<br />
==== properties ====<br />
<br />
It has the following properties:<br />
<br />
* '''Fields''' (TSQLDBRestFieldList) A list of fields that will be included in the output, or can be supplied in the input.<br />
* '''Enabled''' (Boolean) Is this resource enabled ?<br />
* '''InMetadata''' (Boolean) Should this resource be shown in the metadata list ?<br />
* '''ConnectionName''' (UTF8String) Connection name to use for this resource. <br />
* '''TableName''' (UTF8String) Database table name. This must only be specified if the SQL is auto generated.<br />
* '''ResourceName''' (UTF8String) Name of this resource as exposed to the outside world.<br />
* '''AllowedOperations''' (TRestOperations) The operations allowed on this resources (Get/Put/Post/Delete/Options/Head)<br />
* '''SQLSelect''' (TStrings) SQL select statement for a GET request. Left empty, it is autogenerated based on the table name. The where clause uses the fields with the ''foInKey'' flag set for single resources.<br />
* '''SQLInsert''' (TStrings) SQL INSERT statement for a POST request. Left empty, it is autogenerated based on the table name. <br />
* '''SQLUpdate''' (TStrings) SQL UPDATE statement for a PUT request. Left empty, it is autogenerated based on the table name. The where clause uses the fields with the ''foInKey'' flag set.<br />
* '''SQLDelete''' (TStrings) SQL DELETE statement for a DELETE request. Left empty, it is autogenerated based on the table name. The where clause uses the fields with the ''foInKey'' flag set.<br />
* '''SQL'' an array of SQL statements, based on the previous ones.<br />
* '''BusinessProcessor''' (TSQLDBRestCustomBusinessProcessor) at runtime this contains the business processor for this resource (if any)<br />
<br />
==== Events ====<br />
<br />
The following events are available:<br />
<br />
* '''OnResourceAllowed''' (TSQLDBRestAllowResourceEvent) Called to check whether the user is allowed to access this resource.<br />
* '''OnAllowedOperations''' (TSQLDBRestAllowedOperationsEvent) Called to allow to fine-tune the allowed operations for the user.<br />
* '''OnGetDataset''' (TSQLDBRestGetDatasetEvent) You can return a custom dataset for GET operations. <br />
* '''OnCheckParams''' (TSQLDBRestCheckParamsEvent) You can check the INSERT/UPDATE/DELETE SQL Statement parameters in this request/<br />
* '''OnAllowRecord''' (TSQLDBRestAllowRecordEvent) Called for each record, you can ddecide whether the record should be included in the output.<br />
<br />
Note: when set, the corresponding event in the business processor is not triggered.<br />
<br />
==== Methods ====<br />
<br />
The following methods exist:<br />
Function GetDataset(aContext : TBaseRestContext; aFieldList : TRestFieldPairArray; aOrderBy : TRestFieldOrderPairArray; aLimit, aOffset : Int64) : TDataset;<br />
Get the custom dataset for this resource.<br />
function GenerateDefaultSQL(aKind: TSQLKind): UTF8String; virtual;<br />
Get the default SQL for this resource if none was specified.<br />
function GetFieldList(aListKind: TFieldListKind): UTF8String;<br />
Return a list of fields as a string, separated by a correct separator token <br />
function GetFieldArray(aListKind: TFieldListKind): TSQLDBRestFieldArray;<br />
Return a list of fields, similar to GetFieldList, but returns the raw fields.<br />
Function GetResolvedSQl(aKind : TSQLKind; Const AWhere : UTF8String; Const aOrderBy : UTF8String = ''; aLimit : UTF8String = '') : UTF8String;<br />
Return the actual SQL Statement that will be executed, with macros <br />
Procedure PopulateFieldsFromFieldDefs(Defs : TFieldDefs; aIndexFields : TStringArray; aProcessIdentifier : TProcessIdentifier; aMinFieldOpts : TRestFieldOptions);<br />
Populate the fields collection from a TFieldDefs collection.<br />
<br />
=== TSQLDBRestField ===<br />
The TSQLDBREstResource has a collection of fields (TSQLDBRestField) that determine the output fields of the REST resource<br />
<br />
It has the following properties;<br />
* FieldName (UTF8String Read FFieldName Write FFieldName;<br />
* PublicName (UTF8String Read GetPublicName Write FPublicName;<br />
* GeneratorName (String) name of a generator to privide a unique value.<br />
* FieldType (TRestFieldType) Data type of the exposed field.One of (the names speak for themselves)<br />
** rftInteger<br />
** rftLargeInt<br />
** rftFloat<br />
** rftDate<br />
** rftTime<br />
** rftDateTime<br />
** rftString<br />
** rftBoolean<br />
** rftBlob (sent as base64-encoded data)<br />
* NativeFieldType (TFieldType) Native database field type of this field.<br />
* Options (TRestFieldOptions) options for this field. One of more of:<br />
** foInKey This is a key field.<br />
** foInInsert The field can be inserted (if statement is autogenerated)<br />
** foInUpdate The field can be updated (if statement is autogenerated) <br />
** foRequired A field value is required.<br />
** foFilter Filtering is allowed on this field.<br />
** foOrderBy Ordering is allowed on this field. <br />
** foOrderByDesc Ordering (descendent) is allowed on this field. <br />
* Filters (TRestFieldFilters) Allowed filter expressions for this field. One or more of:<br />
** rfEqual<br />
** rfLessThan<br />
** rfGreaterThan<br />
** rfLessThanEqual<br />
** rfGreaterThanEqual<br />
** rfNull<br />
* MaxLen (integer) for string fields, the maximum allowed length of the field.<br />
<br />
=== TSQLDBRestBusinessProcessor ===<br />
This class serves to implement any business rules you want to attach to your rest resource.<br />
You can drop one TSQLDBRestBusinessProcessor instance per resource on a module, set the <br />
schema and resource name (a list of resource names is available in the Object Inspector) and implement the events.<br />
<br />
The following properties and events are available:<br />
* '''Schema''' : The schema in which the resource resides.<br />
* '''ResourceName''' The resource for which the business rules are valid.<br />
* '''OnGetDataset''' : Event called when you want to create a custom dataset. You must free the dataset yourself.<br />
* '''OnCheckParams''' : Event in which you can verify whether the parameters for insert/update/delete/select queries are OK.<br />
* '''OnAllowResource''' : Event to decide whether a property is allowed for a request. You can use this e.g. to forbid certain users from accessing a resource.<br />
* '''OnAllowedOperations''' : Event to decide whether a REST operation is allowed for a request. You can use this to forbid certain users from writing to a resource, but allow them to read.<br />
* '''OnAllowRecord''' : Called for each record which will be output. This can be used to prevent certain records from being streamed to the output. Note that if ''Limit'' is specified, this will not be taken into account, i.e. if the limit is 10, and you forbid 2 records from being streamed at this point, only 8 records will be returned.<br />
<br />
All calls get passed a TRestContext instance. This contains 2 properties and a method:<br />
Function GetVariable(Const aName : UTF8String; aSources : TVariableSources; Out aValue : UTF8String) : Boolean;<br />
Call this to get a HTTP Query variable, header,... The function returns true if the variable was found.<br />
Property UserID : UTF8String<br />
This will be set when calling.<br />
Property Data : TObject<br />
You can attach data to this if you want to. It will be kept for the duration of the request.<br />
You are responsible for freeing this data, though.<br />
<br />
== Special Resources ==<br />
<br />
=== metaData ===<br />
<br />
The ''/baseURL/metaData'' resource lists all the resources defined in the schema, together with the allowed operations.<br />
<br />
The usual formats and parameters are supported.<br />
<br />
=== metaData/resourceName ===<br />
<br />
The ''/baseURL/metaData/resourceName'' resource lists all the fields in the '''resourceName''' resource, together with the properties.<br />
<br />
The usual formats and parameters are supported for this resource.<br />
<br />
<br />
=== customview ===<br />
<br />
The customView resource is special. Its availability must be enabled using the ''Dispatcher'''s Options. When ''rdoCustomView'' is included in the options, the <br />
<br />
baseURL/customView<br />
<br />
URL becomes available. It allows the client to specify an SQL Select statement using the ''SQL'' parameter:<br />
<br />
baseURL/customView?SQL=select count(*) as thecount from person<br />
<br />
(you must encode the sql using the usual URI encoding mechanism, it is omitted for readability reasons here)<br />
The usual parameters for fieldlists, format are supported, but not the ''limit'' and ''offset'' parameters.<br />
<br />
The engine will check that only SELECT statements passed on. <br />
Nevertheless, this is an inherently less safe mechanism which should only be used in case of need.<br />
<br />
=== _connection ===<br />
<br />
(Work in progress)<br />
This is a resource that allows you to manage the connections through REST. <br />
This can be useful in development environments, where you can simply create a single binary that does not need any configuration, <br />
the whole configuration can be done through REST, including the management of the available connections.<br />
<br />
Because this is security-wise a dangerous option to enable, it is disabled by default, and must be enabled explicitly with the ''rdoConnectionResource'' option of the dispatcher.<br />
<br />
Use cases are a development environment where this can be used to confiure the restserver from within the IDE. <br />
When moving to production, the option can be disabled, and only fixed and known connections will be allowed/<br />
<br />
== Available Output formats ==<br />
<br />
=== JSON (the default) ===<br />
<br />
* A simple JSON object encapsulating data, metadata and possibly error nodes.<br />
* rows are exported as JSON objects, field names are property names of the object.<br />
* Name: 'json'<br />
* Content-type: 'application/json'<br />
* Input and output.<br />
* Data by default under 'data'<br />
* Metadata by default under 'metaData'<br />
* Errors reported under 'error'<br />
* Property names are configurable in REST dispatcher.<br />
<br />
=== XML (a custom format) ===<br />
<br />
* A simple XML document encapsulating data, metadata and possibly error nodes, under a root element 'datapacket'<br />
* rows are exported as XML elements (named 'row') , the contents as text in the element.<br />
* Name: 'xml'<br />
* Content-type: 'text/xml'<br />
* Input and output.<br />
* Data by default under 'data'<br />
* Metadata by default under 'metadata'<br />
* Errors reported under 'error'<br />
* Element names are configurable in REST dispatcher.<br />
<br />
=== CSV ===<br />
<br />
* Simple comma-separated CSV list.<br />
* Name: 'csv'<br />
* Content-type: 'text/csv'<br />
* Input and output.<br />
* Separator: Comma<br />
* Quotes when needed.<br />
* Metadata means first line contains fieldnames.<br />
<br />
=== CDS ===<br />
<br />
* XML package using the XMLFormat used by Delphi <code>TClientDataset</code>.<br />
* Name: 'cds'<br />
* Content-type: 'text/csv'<br />
* Input and output.<br />
<br />
A Delphi demo program is provided.<br />
<br />
=== ADO ===<br />
<br />
* XML format as used by ADO recordsets (as used by MS Access)<br />
* Name: 'ado'<br />
* Input and output.<br />
<br />
== Ini file Support ==<br />
<br />
The following classes can load/save their configuration settings from/to an .ini file<br />
<br />
* TSQLDBRestDispatcher<br />
* TRestBasicAuthenticator<br />
* TSQLDBRestConnection<br />
* TRestStringsConfig <br />
<br />
The support for this is implemented in 2 units;<br />
* the sqldbauthini unit has a type helper for the TRestBasicAuthenticator class. <br />
* The sqldbrestini unit has the type helpers for TSQLDBRestDispatcher and TSQLDBRestConnection and TRestStringsConfig. <br />
<br />
The helpers implement LoadFromFile/SaveToFile methods, as well as LoadFromIni/SaveToIni methods.<br />
There are some options to control the level of depth.<br />
<br />
The schemas (if so required) are saved as JSON files in the same directory as the .ini file, using the name of the schema as the filename.<br />
<br />
== Lazarus Support ==<br />
<br />
Lazarus support for the SQLDB REST Bridge is in the lazsqldbrest.lpk package, under the fpweb directory. <br />
It registers the TSQLDBRestDispatcher, TSQLDBRestSchema and TRestBasicAuthenticator components on the component palette:<br />
<br />
[[File:sqldbrestcomponents.png]]<br />
<br />
Additionally it registers some component- and property editors:<br />
<br />
Further integration with Lazarus is planned:<br />
1. Add a nicer schema editor<br />
1. Let the IDE act as a REST server. (alternatively, add a tool with a menu entry under tools for easy configuration)<br />
<br />
Lazarus support can work with FPC 3.0.4. <br />
To make this work, the files can be copied from SVN or a 3.2 release to the lazarus fpweb directory. <br />
<br />
To this end, <br />
# create a directory '''src''' below the '''components/fpweb directory''' (where the package is located) <br />
# copy all files in directory<br />
packages/fcl-web/src/restbridge de<br />
: to directory<br />
lazarus/components/fpweb/src<br />
# compile the lazsqldbrest.lpk package. It should find the files in the src directory and use them.<br />
<br />
== Pas2JS Support ==<br />
The SQLDB Rest bridge will be integrated in the compile server that comes with pas2JS:<br />
This means that pas2js will come with a toll that<br />
* Can recompile your web project on the fly<br />
* Can serve files on disc<br />
* Can act as a REST server.<br />
So no actual server is needed during development.<br />
<br />
== Usage ==<br />
<br />
=== In a fcl-web application. ===<br />
<br />
The rest bridge was designed to be simple to use.<br />
<br />
==== Expose a single database, no authentication. ====<br />
<br />
Simple expose of a database. Run the following code in the startup of your program (for example, DoRun of the web application class, or startup code of the program)<br />
<br />
<source lang="delphi">FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.ExposeDatabase(<br />
'postgres','localhost','expensetracker','me','secret'<br />
,Nil,<br />
[foFilter,foInInsert,foInUpdate,foOrderByDesc]);<br />
FDisp.Active:=True; <br />
FDisp.SaveToFile('demo.ini');</source><br />
This exposes all tables of the database 'expenstracker', allows to filter,update and sort on all fields. The connection will be named 'Connection1', the schema 'SchemaConnection1'<br />
<br />
The configuration will be saved in a file called 'demo.ini'.<br /><br />
It can be reused to quickly set up a REST dispatcher.<br />
<br />
==== Expose single database, HTTP Basic authentication. ====<br />
<br />
Similar to the previous example, but with HTTP BASIC authentication for a single user:<br />
<br />
<source lang="delphi">FAuth:=TRestBasicAuthenticator.Create(Self);<br />
FAuth.DefaultUserName:='me';<br />
FAuth.DefaultPassword:='secret';<br />
FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.Authenticator:=Fauth;<br />
FDisp.ExposeDatabase(<br />
'postgres','localhost','expensetracker','me','secret'<br />
,Nil,<br />
[foFilter]);<br />
FDisp.Active:=True; </source><br />
This exposes all tables of the database 'expenstracker', allows to filter,update and sort on all fields. The connection will be named 'Connection1', the schema 'SchemaConnection1'. No updates are possible: the fields will not be in update/insert statements. Sorting is also not allowed.<br />
<br />
==== Expose single database, HTTP Basic authentication (2) ====<br />
<br />
Smilar to the previous example, but with HTTP BASIC authentication, using the database to authenticate a user:<br />
<br />
<source lang="delphi">FAuth:=TRestBasicAuthenticator.Create(Self);<br />
FAuth.AuthenticateUserSQL.Text:='select uID from users where (uLogin=:UserName) and (uPassword=:Password)';<br />
FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.Authenticator:=Fauth;<br />
FDisp.ExposeDatabase(<br />
'postgres','localhost','expensetracker','me','secret'<br />
,['projects','expenses'],<br />
[]);<br />
FDisp.Active:=True; </source><br />
Here only the 'projects' and 'expenses' tables are exposed, read-only.<br />
<br />
==== Using a Config file and schema files. ====<br />
<br />
Start a dispatcher, loading the file demo.ini. The dioSkipReadSchemas tells it not to load schema files, the schemas will be created from the connections.<br />
<br />
<source lang="delphi">FDisp:=TSQLDBRestDispatcher.Create(Self);<br />
FDisp.LoadFromFile('demo.ini',[dioSkipReadSchemas]);<br />
FDisp.Active:=True;</source><br />
The .ini file can be created using SaveToFile on a configured dispatcher instance.<br />
<br />
=== In the lazarus IDE. ===<br />
<br />
To work in the lazarus IDE with the SQLDB Rest bridge, you must install the '''lazsqldbrest''' package, which can be found in the directory components/fpWeb.<br />
<br />
<br />
==== Using a datamodule ====<br />
<br />
In this approach, a datamodule is created that is in memory throughout the whole lifetime of the application (fcl-web application)<br />
<br />
<br />
# Create a HTTP server application or FCGI application. (a CGI will also work but is very inefficient)<br />
# Add a plain datamodule to the application.<br />
# Drop a TSQLDBRestSchema class from the 'FCL-Web' tab of the component palette<br />
# Edit the schema (property resources), or import it from file using the component editor context menu<br />
# Drop a TSQLDBRestDispatcher class from the 'FCL-Web' tab of the component palette<br />
# Edit the connections<br />
# Add an item to the Schemas property and point it to the TSQLDBRestSchema added previously <br />
# For HTTP Basic authentication, add a TRESTBAsicAuthenticator class, and set the TSQLDBRestDispatcher instance's Authentocator property to it. <br />
<br />
The end result should look more or less like this:<br />
<br />
[[File:restmodule.png]]<br />
<br />
That's it. The project is ready to run.<br />
<br />
==== Using a SQLDBRestdatamodule ====<br />
<br />
This approach is basically the same as the previous one.<br />
<br />
# Create a HTTP application<br />
# Under File - New, create a SQLDB Rest Bridge Module.<br />
# Drop a SQLDBRestDispatcher component on the module.<br />
# The Dispatcher property of the module should be set to the SQLDBRestDispatcher component.<br />
# Set up the component as required, as in the previous example.<br />
<br />
== Examples ==<br />
<br />
=== FPC ===<br />
* in FPC Packages, the fcl-web/examples/restbridge directory, there is a small example that serves a expenses tracker database.<br />
* The directory (delphiclient) below contains a small Delphi project that shows how to connect a TClientDataset to the SQLDB Rest bridge.<br />
: Design time view:<br />
: [[File:delphirestclientdesign.png]]<br />
: Run time view:<br />
: [[File:delphirestclient.png]]<br />
<br />
=== Lazarus ===<br />
There are 2 server examples available, and several client examples.<br />
The client examples are all the same, they just use a different kind of ''TDataset'' to show the data coming from the server: the '''buf''', '''json''' and '''CSV''' formats are demonstrated in this way.<br />
==== Server examples ====<br />
<br />
* The demo/restbridge directory of Lazarus/Components contains the same example program, using a datamodule.<br />
* The demo/restmodule directory of Lazarus/Components contains the same example program, using a SQLDB Rest Web datamodule.<br />
<br />
==== TBufDataset client example ====<br />
* The demo/bufclient directory contains a demo that shows how to load data from a SQLDB REST bridge server in a TBUFDataset dataset. It uses the 'buf' format to do so.<br />
: [[File:sqldbrestbufclientdata.png]]<br />
<br />
: [[File:sqldbrestbufclientraw.png]]<br />
: it also demonstrates how to use the metadata resource of the server to list the available resources<br />
==== TJSONDataset client example ====<br />
<br />
* The demo/jsonclient directory contains a demo that is similar to the bufclient demo: <br />
: it shows how to load data from a SQLDB REST bridge server in a TBaseJSONDataset dataset, using the 'csv' format<br />
: [[File:sqldbrestjsonclientdata.png]]<br />
<br />
: [[File:sqldbrestjsonclientraw.png]]<br />
: Like the bufdataset demo, it also demonstrates how to use the metadata resource of the server to list the available resources<br />
<br />
==== TCSVDataset client example ====<br />
* The demo/csvclient directory contains a demo that is similar to the bufclient demo: <br />
: it shows how to load data from a SQLDB REST bridge server in a TCSVDataset dataset, using the 'csv' format<br />
<br />
=== Pas2JS ===<br />
* A similar application to the lazarus bufflien/jsonclient programs exists for pas2JS in demo/restbridge/simple<br />
: [[File:sqldbrestbridgepas2js.png]]<br />
<br />
== TODO ==<br />
<br />
The following extensions are still planned:<br />
<br />
* Support for <code>?q=filterexpression</code> in URL filters.<br />
* Connection management API. (zero-config service)<br />
* Use HTTP credentials to connect to the database.<br />
* Unknown query params are now ignored. Allow to check for valid query parameters and raise error if unknown query param is encountered.<br />
<br />
<br />
[[Category:Databases/ru]]<br />
{{AutoCategory}}</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157303lNet/ru2023-09-11T16:10:20Z<p>Zoltanleo: /* API changes */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Тестирование==<br />
<br />
Невизуальные консольные компоненты были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==История изменений==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Надежность кода ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Дорожная карта ==<br />
<s>Зачеркнутые</s> пункты в указанном варианте уже реализованы:<br />
* Стресс-тестирование [всегда]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Добавление протокола ICMP Ping/Traceroute [na] (отменено по разным причинам*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Добавление Telnet и FTP серверов (экспериментальный уровень) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Добавлена поддержка набора виджетов Carbon и/или Cocoa [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki>Для PING требуются необработанные сокеты, которые доступны только root на платформах unix/linux. Было решено, что lNet не будет поддерживать это, пользователи, которым нужен PING, должны использовать программы ping и ping6 через TProcess.<br />
<br />
== Требования ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== Изменения в API ==<br />
<br />
'''0.1 до 0.2'''<br />
* Были внесены некоторые изменения в API с версии 0.1 до 0.2. Это было необходимо и должно улучшить удобство использования пакетов. Я знаю, что изменения API — это всегда головная боль (PITA - аббр. pain in the ass), но с этого момента я постараюсь свести их к минимуму. Версия 0.3 будет первой бета-версией, API которой останется неизменным (возможно будет только добавление новых вещей).<br />
* <code>OnRecieve()</code> изменен на <code>OnReceive()</code>.<br />
* <code>Accept()</code> изменен на <code>Listen()</code>.<br />
* Аргументы всех событий изменены. <code>SocketNumber(int)</code> был изменен на <code>aSocket(TLSocket)</code> по логическим соображениям и причинам скорости.<br />
<br />
'''0.2 до 0.3'''<br />
* Добавлены <code>OnCanSend</code> и <code>OnConnect</code>. Connect теперь не блокируется.<br />
* Удалены <code>buffersize</code> и <code>maxmsgs</code>. <code>buffersize</code> больше не требуется, поскольку внутренний буфер не используется.<br />
<br />
'''0.3 до 0.4'''<br />
* Множество дополнений. Единственная «поломка» заключается в том, что <code>TLSocket.Port</code> был изменен на <code>TLSocket.LocalPort</code> и <code>TLSocket.PeerPort</code> по соображениям согласованности.<br />
<br />
'''0.4 до 0.5'''<br />
* Мы провели некоторую очистку API, которая привела к поломке API.<br />
* Все классы "connection" теперь происходят от TLComponent.<br />
* <code>TLSocket</code> теперь имеет свойство <code>.Creator</code>, который является свойством <code>TLComponent</code>, определяющим создателя (например, <code>TLTcp</code>), поэтому теперь вы можете видеть, какой сокет в событии принадлежит какому соединению.<br />
* Добавлены вызовы без параметров <code>Connect()</code> и <code>Listen()</code> для всех соединений. В основном это делается для того, чтобы включить визуальный стиль Lazarus, чтобы люди могли использовать свойства <code>.Host</code> и <code>.Port</code> и неоднократно просто вызывать, например, <code>.Connect()</code>.<br />
* Добавлен набор <code>StatusSet</code> для компонентов SMTP и FTP. Это позволяет отслеживать определенные действия FTP или SMTP и их результаты с помощью событий <code>OnSuccess</code> и <code>OnFailure</code>. Вы устанавливаете события для просмотра в наборе и получаете результаты в одном из обратных вызовов.<br />
* <b>Изменены ВСЕ обратные вызовы, которые не использовали TLSocket в качестве отправителя, чтобы использовать его. Это означает, что все события FTP, SMTP и HTTP, которые имели стиль "Sender: TL<HigherType>", были возвращены к базовым обратным вызовам в стиле Socket!</b><br />
<br />
'''0.5.4- до 0.5.5+'''<br />
* Были внесены некоторые незначительные изменения в API, которые не должны ничего нарушить, но я перечисляю их на всякий случай.<br />
* Свойство <code>.Timeout</code> для событий и соединений было задано как Integer (из DWord) для поддержки -1 как «блокировки до тех пор, пока не произойдет событие».<br />
<br />
'''0.5 до 0.6'''<br />
* Никаких изменений в публичном API, только дополнения.<br />
<br />
{{Note | В версии 0.4.0 я забыл переместить модуль lHTTPSettings из библиотеки в пример http. Он НЕ должен был быть частью библиотеки, просто примером. НЕ используйте это, это не «библиотека», это часть примера (http-сервер получит собственный проект позже)<br />
}}<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157302lNet/ru2023-09-11T15:52:05Z<p>Zoltanleo: /* Requirements */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Тестирование==<br />
<br />
Невизуальные консольные компоненты были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==История изменений==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Надежность кода ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Дорожная карта ==<br />
<s>Зачеркнутые</s> пункты в указанном варианте уже реализованы:<br />
* Стресс-тестирование [всегда]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Добавление протокола ICMP Ping/Traceroute [na] (отменено по разным причинам*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Добавление Telnet и FTP серверов (экспериментальный уровень) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Добавлена поддержка набора виджетов Carbon и/или Cocoa [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki>Для PING требуются необработанные сокеты, которые доступны только root на платформах unix/linux. Было решено, что lNet не будет поддерживать это, пользователи, которым нужен PING, должны использовать программы ping и ping6 через TProcess.<br />
<br />
== Требования ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157301lNet/ru2023-09-11T15:51:37Z<p>Zoltanleo: /* Roadmap */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Тестирование==<br />
<br />
Невизуальные консольные компоненты были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==История изменений==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Надежность кода ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Дорожная карта ==<br />
<s>Зачеркнутые</s> пункты в указанном варианте уже реализованы:<br />
* Стресс-тестирование [всегда]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Добавление протокола ICMP Ping/Traceroute [na] (отменено по разным причинам*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Добавление Telnet и FTP серверов (экспериментальный уровень) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Добавлена поддержка набора виджетов Carbon и/или Cocoa [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki>Для PING требуются необработанные сокеты, которые доступны только root на платформах unix/linux. Было решено, что lNet не будет поддерживать это, пользователи, которым нужен PING, должны использовать программы ping и ping6 через TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157300lNet/ru2023-09-11T15:46:45Z<p>Zoltanleo: /* Stability */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Тестирование==<br />
<br />
Невизуальные консольные компоненты были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==История изменений==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Надежность кода ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157299lNet/ru2023-09-11T15:45:51Z<p>Zoltanleo: /* History */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Тестирование==<br />
<br />
Невизуальные консольные компоненты были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==История изменений==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157298lNet/ru2023-09-11T15:45:29Z<p>Zoltanleo: /* тестирование */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Тестирование==<br />
<br />
Невизуальные консольные компоненты были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157275lNet/ru2023-09-05T15:54:32Z<p>Zoltanleo: /* See also */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==тестирование==<br />
Невизуальные компоненты консоли были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== См.также ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157274lNet/ru2023-09-05T15:54:05Z<p>Zoltanleo: /* lNet for FreeBSD, Linux, macOS and Windows */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==тестирование==<br />
Невизуальные компоненты консоли были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet для FreeBSD, Linux, macOS и Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet для FreeBSD, Linux, macOS и Windows] от trevoz<br />
** Множество изменений: теперь библиотека будет компилироваться с помощью FPC 3.2.2 или FPC 3.3.1.<br />
** Добавлен TLSv1.3.<br />
** Linux TLS не работает из-за проблемы с e-poll в файле <tt>lib/sys/lepolleventer.inc</tt>.<br />
** Примеры консоли компилируются (я протестировал пример SMTP, который мне и нужен).<br />
** Примеры визуальных компонентов не компилируются для macOS — в них нет привязок интерфейса Cocoa или даже несуществующего Carbon.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157273lNet/ru2023-09-05T15:49:50Z<p>Zoltanleo: /* Ubuntu special notice */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==тестирование==<br />
Невизуальные компоненты консоли были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Специальное уведомление под Ubuntu ===<br />
<br />
Некоторые «отсутствующие файлы» фактически находятся в подпапке «/net» и не обнаруживаются компилятором fpc. Просто скопируйте недостающие файлы из подпапки (lnet.pas, tomwinsock.pas, lfunc.inc...) '/usr/lib/lazarus/comComponents/lnetpackage/lnet' в '/usr/lib/lazarus/comComponents/lnetpackage'. ' (при необходимости измените пути). Затем завершите настройку, следуя процедуре установки. (от Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157272lNet/ru2023-09-05T15:48:46Z<p>Zoltanleo: /* Stability */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==тестирование==<br />
Невизуальные компоненты консоли были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP и Telnet по-прежнему нуждаются в некоторых дополнительных функциях, таких как методы аутентификации.<br />
<br />
В Linux, когда вы начинаете использовать LNet, GDB может случайно (при компиляции) аварийно завершить работу с различными типами сообщений. Установка для отладчика значения «Автоматически» в параметрах пакетов lnetbase и lnetvisual устраняет проблему.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157271lNet/ru2023-09-05T15:47:40Z<p>Zoltanleo: /* Testing */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==тестирование==<br />
Невизуальные компоненты консоли были протестированы на Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) и FreeBSD_x86_32.<br />
Визуальные компоненты (пакеты Lazarus) были протестированы в Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 и FreeBSD_x86_32. (gtk1 и gtk2 на unix платформах)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157270lNet/ru2023-09-05T15:37:44Z<p>Zoltanleo: /* Package contents */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Содержимое пакета==<br />
<br />
Пакет состоит из базовой библиотеки модулей lNet, lTelnet для протокола telnet, lFTP для протокола ftp и библиотек lNetComponents для предоставления визуальных и невизуальных компонентов для работы в сети. Начиная с версии 0.4.0 были добавлены компоненты lHTTP и lSMTP. Начиная с версии 0.5.1 в библиотеке есть поддержка MIME, в основном для создания составных сообщений для SMTP. Начиная с версии 0.6.0 lNet поддерживает SSL по модульному принципу, а протокол SMTP был расширен до полной поддержки SSL/TLS (включая согласование STARTTLS во время выполнения). Клиентская часть HTTPS также поддерживается.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157269lNet/ru2023-09-05T15:19:01Z<p>Zoltanleo: /* Installation */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Установка визуального пакета в Lazarus ==<br />
* запускаем Lazarus, перейдите в раздел «Компоненты/откройте файл пакета» (*.lpk).<br />
* открываем lnetvisual.lpk и устанавливаем<br />
* перезапускаем Лазарус<br />
<br />
Если вы хотите использовать LNet в своем проекте, просто скопируйте куда-нибудь каталог ''lib'' и поместите его в путь поиска модулей компилятора с параметрами -Fu и -Fi для подкаталога ''lib/sys''.<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157268lNet/ru2023-09-05T15:12:33Z<p>Zoltanleo: /* Документация */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть [https://github.com/almindor/lnet/tree/master/examples демки] и [https://github.com/almindor/lnet/blob/master/doc/en/doc_lnet.xml документация] в виде xml-файла<br />
----<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157267lNet/ru2023-09-05T15:10:57Z<p>Zoltanleo: /* Documentation */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Документация ==<br />
<br />
Документация по lNet частично завершена. Вы также можете использовать диаграммы классов Jesus и модулей в качестве основного справочника.<br />
<br />
Документация по протоколам eventer и TCP/UDP.: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Обзорные схемы можно найти здесь.: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Зеркало документов и текущие заметки: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: большинство ссылок старые, потому - битые. На гитхабе есть демки и документация<br />
----<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157266lNet/ru2023-09-05T14:48:15Z<p>Zoltanleo: /* Events of TLNetComponent */</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===События TLNetComponent===<br />
<br />
* <code>OnError</code> - это событие вызывается всякий раз, когда возникает ошибка соединения. Если обработчик не назначен, возникает исключение с "msg". Аргументы: <code>msg</code> — содержит строковое и числовое представление сообщения об ошибке, отформатированное в собственной кодировке или UTF8 (в зависимости от свойства <code>ForceUTF8</code>). <code>aSocket</code> — это сокет, в котором произошла ошибка, ноль означает, что ошибка не связана с конкретным сокетом.<br />
<br />
* <code>OnConnect</code> - это событие генерируется при успешном подключении клиента. <code>aSocket</code> — это сокет, к которому подключились. Он должен служить «отправной точкой» для клиентов.<br />
<br />
* <code>OnAccept</code> - это событие вызывается всякий раз, когда на сервере принимается соединение. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, который принял соединение.<br />
<br />
* <code>OnDisconnect</code> - это событие вызывается всякий раз, когда соединение разорвано. Если обработчик не назначен, ничего не происходит. <code>aSocket</code> содержит сокет, соединение с которым было разорвано.<br />
<br />
* <code>OnReceive</code> - это событие вызывается всякий раз, когда новые данные «готовы к приему». Если обработчик не назначен, ничего не происходит, но может произойти заполнение системного буфера, что, в свою очередь, вызовет событие <code>OnError</code>. <code>aSocket</code> — сокет, через который были получены данные. Данные считываются методом <code>Get</code>/<code>GetMessage</code>.<br />
<br />
* <code>OnCanSend</code> - это событие вызывается всякий раз, когда новые данные могут быть снова отправлены в сокет после сбоя вызова функции отправки и возврата 0. Его можно использовать для автоматизации отправки больших фрагментов. <code>aSocket</code> — это сокет, через который можно вызвать отправку.<br />
<br />
== Documentation ==<br />
<br />
Documentation of lNet is partially done. You can also use Jesus' diagrams of classes and units as basic reference.<br />
<br />
Documentation of eventer and TCP/UDP protocols: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Overview diagrams can be found here: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Docs Mirror and Ongoing Notes: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet&diff=157265lNet2023-09-05T14:33:54Z<p>Zoltanleo: </p>
<hr />
<div>{{LNet}}<br />
<br />
==About== <br />
'''lNet''' or '''Lightweight Networking Library''' is a collection of classes and components to enable event-driven TCP or UDP networking.<br />
<br />
Authors:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
License: modified LGPL license (permits static linking).<br />
<br />
==Download==<br />
There are two variants of the library, which are no longer completely synchronised:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Packaged as utilities for the use of the FPC project itself. They are not recommended for general use.<br />
* At http://lnet.wordpress.com, however this advises that the project has been moved to GitHub https://github.com/almindor/lnet.<br />
<br />
You can contact the author directly by email or go to #lnet channel on freenode.<br />
<br />
== Usage ==<br />
<br />
Drop a TLTCPComponent or TLUDPComponent on a form. Assign OnAccept, OnError, OnReceive, OnConnect and OnDisconnect event handlers. Note that this is optional but in most<br />
cases required for functionality.<br />
<br />
Client connection is initialized with the Connect call. Arguments are IP address/hostname and port respectivly. Non-blocking connect is used, this means that you '''DON'T''' know that you're connected until OnConnect event is fired.<br />
<br />
Server connection is initialized with the Listen* call. Argument is port.<br />
<br />
{{Note| address is a string representation of IP eg: '127.0.0.1' or a hostname like 'localhost'.}}<br />
<br />
Sending data to the other side is accomplished with the Send/SendMessage method.<br />
<br />
===Events of TLNetComponent===<br />
<br />
* OnError - this event is fired whenever a connection error occurs. If no handler is assigned an excepion with "msg" is raised. Arguments are: msg - contains string and numerical representation of error message formatted in native encoding or UTF8 (depending on ForceUTF8 property). aSocket is the socket at which the error occured, nil means that the error is not a socket specific one.<br />
<br />
* OnConnect - This event is fired when the client succesfuly connects. aSocket is the socket which connected. It should serve as the "start" point for clients.<br />
<br />
* OnAccept - this event is fired whenever a connection is accepted on a server. If no handler is assigned nothing is done. aSocket contains the socket which accepted the connection.<br />
<br />
* OnDisconnect - this event is fired whenever a connection is lost. If no handler is assigned nothing is done. aSocket contains the socket which got disconnected.<br />
<br />
* OnReceive - This event is fired whenever new data is '''ready to be received'''. If no handler is assigned nothing is done, but a system buffer fill may happen which will in turn fire an OnError event. aSocket is the socket at which the data got received. Data is read by Get/GetMessage method.<br />
<br />
* OnCanSend - This event is fired whenever new data can be sent again on a socket, after a call to send function failed, returning 0. It can be used to automate sending of big chunks. aSocket is socket on which send can be called.<br />
<br />
== Documentation ==<br />
<br />
Documentation of lNet is partially done. You can also use Jesus' diagrams of classes and units as basic reference.<br />
<br />
Documentation of eventer and TCP/UDP protocols: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Overview diagrams can be found here: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Docs Mirror and Ongoing Notes: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet/ru&diff=157264lNet/ru2023-09-05T14:33:40Z<p>Zoltanleo: Created page with "{{LNet}} ==О компоненте== '''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонен..."</p>
<hr />
<div>{{LNet}}<br />
<br />
==О компоненте==<br />
'''lNet''' или '''Lightweight Networking Library''' представляет собой набор классов и компонентов для включения управляемых событиями сетей TCP или UDP.<br />
<br />
Авторы:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
Лицензия: modified LGPL license (разрешает статическое связывание).<br />
<br />
==Загрузка==<br />
<br />
Существует два варианта библиотеки, которые больше полностью не синхронизируются:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Упакованы в виде утилит для использования самого проекта FPC. Они не рекомендуются для общего использования.<br />
* https://lnet.wordpress.com/ (перемещен на [https://github.com/almindor/lnet GitHub]).<br />
<br />
Вы можете связаться с автором напрямую по электронной почте или перейти на канал #lnet на freenode.<br />
<br />
== Использование ==<br />
<br />
Бросьте TLTCPComponent или TLUDPComponent на форму. Назначьте обработчики событий OnAccept, OnError, OnReceive, OnConnect и OnDisconnect. Обратите внимание, что это необязательно, но в большинстве случаев требуется для реализации функционала.<br />
<br />
Клиентское соединение инициализируется вызовом Connect. Аргументами являются IP-адрес/имя хоста и порт соответственно. Используется неблокирующее соединение, это означает, что вы «НЕ» знаете, что вы подключены, пока случится событие OnConnect.<br />
<br />
Соединение с сервером инициализируется вызовом Listen. Аргумент — порт.<br />
<br />
{{Note| адрес — это строковое представление IP-адреса, например: '127.0.0.1' или имя хоста, например 'localhost'.}}<br />
<br />
Отправка данных другой стороне осуществляется с помощью метода Send/SendMessage.<br />
<br />
===Events of TLNetComponent===<br />
<br />
* OnError - this event is fired whenever a connection error occurs. If no handler is assigned an excepion with "msg" is raised. Arguments are: msg - contains string and numerical representation of error message formatted in native encoding or UTF8 (depending on ForceUTF8 property). aSocket is the socket at which the error occured, nil means that the error is not a socket specific one.<br />
<br />
* OnConnect - This event is fired when the client succesfuly connects. aSocket is the socket which connected. It should serve as the "start" point for clients.<br />
<br />
* OnAccept - this event is fired whenever a connection is accepted on a server. If no handler is assigned nothing is done. aSocket contains the socket which accepted the connection.<br />
<br />
* OnDisconnect - this event is fired whenever a connection is lost. If no handler is assigned nothing is done. aSocket contains the socket which got disconnected.<br />
<br />
* OnReceive - This event is fired whenever new data is '''ready to be received'''. If no handler is assigned nothing is done, but a system buffer fill may happen which will in turn fire an OnError event. aSocket is the socket at which the data got received. Data is read by Get/GetMessage method.<br />
<br />
* OnCanSend - This event is fired whenever new data can be sent again on a socket, after a call to send function failed, returning 0. It can be used to automate sending of big chunks. aSocket is socket on which send can be called.<br />
<br />
== Documentation ==<br />
<br />
Documentation of lNet is partially done. You can also use Jesus' diagrams of classes and units as basic reference.<br />
<br />
Documentation of eventer and TCP/UDP protocols: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Overview diagrams can be found here: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Docs Mirror and Ongoing Notes: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet&diff=157263lNet2023-09-05T14:20:02Z<p>Zoltanleo: </p>
<hr />
<div>{{LNet}}<br />
<br />
==About==<br />
'''lNet''' or '''Lightweight Networking Library''' is a collection of classes and components to enable event-driven TCP or UDP networking.<br />
<br />
Authors:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
License: modified LGPL license (permits static linking).<br />
<br />
==Download==<br />
There are two variants of the library, which are no longer completely synchronised:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Packaged as utilities for the use of the FPC project itself. They are not recommended for general use.<br />
* At http://lnet.wordpress.com, however this advises that the project has been moved to GitHub https://github.com/almindor/lnet.<br />
<br />
You can contact the author directly by email or go to #lnet channel on freenode.<br />
<br />
== Usage ==<br />
<br />
Drop a TLTCPComponent or TLUDPComponent on a form. Assign OnAccept, OnError, OnReceive, OnConnect and OnDisconnect event handlers. Note that this is optional but in most<br />
cases required for functionality.<br />
<br />
Client connection is initialized with the Connect call. Arguments are IP address/hostname and port respectivly. Non-blocking connect is used, this means that you '''DON'T''' know that you're connected until OnConnect event is fired.<br />
<br />
Server connection is initialized with the Listen* call. Argument is port.<br />
<br />
{{Note| address is a string representation of IP eg: '127.0.0.1' or a hostname like 'localhost'.}}<br />
<br />
Sending data to the other side is accomplished with the Send/SendMessage method.<br />
<br />
===Events of TLNetComponent===<br />
<br />
* OnError - this event is fired whenever a connection error occurs. If no handler is assigned an excepion with "msg" is raised. Arguments are: msg - contains string and numerical representation of error message formatted in native encoding or UTF8 (depending on ForceUTF8 property). aSocket is the socket at which the error occured, nil means that the error is not a socket specific one.<br />
<br />
* OnConnect - This event is fired when the client succesfuly connects. aSocket is the socket which connected. It should serve as the "start" point for clients.<br />
<br />
* OnAccept - this event is fired whenever a connection is accepted on a server. If no handler is assigned nothing is done. aSocket contains the socket which accepted the connection.<br />
<br />
* OnDisconnect - this event is fired whenever a connection is lost. If no handler is assigned nothing is done. aSocket contains the socket which got disconnected.<br />
<br />
* OnReceive - This event is fired whenever new data is '''ready to be received'''. If no handler is assigned nothing is done, but a system buffer fill may happen which will in turn fire an OnError event. aSocket is the socket at which the data got received. Data is read by Get/GetMessage method.<br />
<br />
* OnCanSend - This event is fired whenever new data can be sent again on a socket, after a call to send function failed, returning 0. It can be used to automate sending of big chunks. aSocket is socket on which send can be called.<br />
<br />
== Documentation ==<br />
<br />
Documentation of lNet is partially done. You can also use Jesus' diagrams of classes and units as basic reference.<br />
<br />
Documentation of eventer and TCP/UDP protocols: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Overview diagrams can be found here: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Docs Mirror and Ongoing Notes: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Template:LNet&diff=157262Template:LNet2023-09-05T14:19:44Z<p>Zoltanleo: </p>
<hr />
<div><noinclude><br />
This template used for inserting language bar to pages and categories.<br />
<br />
Read more: [[LNet#Template:LanguageBar]]<br />
<br />
[[Category:Templates for language bar]]<br />
<br />
</noinclude><includeonly>{{#ifeq:{{NAMESPACE}}|Template|<br />
{{#if:{{{1|}}}|{{PreviewLngBar|ExactName={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{PreviewLngBar|PossibleName={{NameWithoutLngSuffix|{{PAGENAME}}}}}}}}|<br />
{{#if:{{{1|}}}|{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{FULLPAGENAME}}}}}}}}}}</includeonly></div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Template:LNet&diff=157261Template:LNet2023-09-05T14:18:46Z<p>Zoltanleo: Created page with "<noinclude> This template used for inserting language bar to pages and categories. Read more: Templates-2015#Template:LanguageBar Category:Templates for language bar..."</p>
<hr />
<div><noinclude><br />
This template used for inserting language bar to pages and categories.<br />
<br />
Read more: [[Templates-2015#Template:LanguageBar]]<br />
<br />
[[Category:Templates for language bar]]<br />
<br />
</noinclude><includeonly>{{#ifeq:{{NAMESPACE}}|Template|<br />
{{#if:{{{1|}}}|{{PreviewLngBar|ExactName={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{PreviewLngBar|PossibleName={{NameWithoutLngSuffix|{{PAGENAME}}}}}}}}|<br />
{{#if:{{{1|}}}|{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{FULLPAGENAME}}}}}}}}}}</includeonly></div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Template:lNet&diff=157256Template:lNet2023-09-05T14:09:35Z<p>Zoltanleo: </p>
<hr />
<div><noinclude><br />
This template used for inserting language bar to pages and categories.<br />
<br />
Read more: [[Templates#Template:LanguageBar]]<br />
<br />
[[Category:Templates for language bar]]<br />
<br />
</noinclude><includeonly>{{#ifeq:{{NAMESPACE}}|Template|<br />
{{#if:{{{1|}}}|{{PreviewLngBar|ExactName={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{PreviewLngBar|PossibleName={{NameWithoutLngSuffix|{{PAGENAME}}}}}}}}|<br />
{{#if:{{{1|}}}|{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{FULLPAGENAME}}}}}}}}}}</includeonly></div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Template:lNet&diff=157255Template:lNet2023-09-05T14:08:45Z<p>Zoltanleo: Created page with "<noinclude> This template used for inserting language bar to pages and categories. Read more: Templates-2015#Template:LanguageBar Category:Templates for language bar..."</p>
<hr />
<div><noinclude><br />
This template used for inserting language bar to pages and categories.<br />
<br />
Read more: [[Templates-2015#Template:LanguageBar]]<br />
<br />
[[Category:Templates for language bar]]<br />
<br />
</noinclude><includeonly>{{#ifeq:{{NAMESPACE}}|Template|<br />
{{#if:{{{1|}}}|{{PreviewLngBar|ExactName={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{PreviewLngBar|PossibleName={{NameWithoutLngSuffix|{{PAGENAME}}}}}}}}|<br />
{{#if:{{{1|}}}|{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{{1}}}}}}}|<br />
{{ShowLngBar|NameWithoutSuffix={{NameWithoutLngSuffix|{{FULLPAGENAME}}}}}}}}}}</includeonly></div>Zoltanleohttps://wiki.freepascal.org/index.php?title=lNet&diff=157253lNet2023-09-05T14:06:29Z<p>Zoltanleo: </p>
<hr />
<div>{{lNet}}<br />
<br />
==About==<br />
'''lNet''' or '''Lightweight Networking Library''' is a collection of classes and components to enable event-driven TCP or UDP networking.<br />
<br />
Authors:<br />
* Ales Katona ([[User: Almindor]])<br />
* Micha Nelissen<br />
<br />
License: modified LGPL license (permits static linking).<br />
<br />
==Download==<br />
There are two variants of the library, which are no longer completely synchronised:<br />
<br />
* [http://sourceforge.net/projects/lazarus-ccr/files/lNet/ lNet package]. Packaged as utilities for the use of the FPC project itself. They are not recommended for general use.<br />
* At http://lnet.wordpress.com, however this advises that the project has been moved to GitHub https://github.com/almindor/lnet.<br />
<br />
You can contact the author directly by email or go to #lnet channel on freenode.<br />
<br />
== Usage ==<br />
<br />
Drop a TLTCPComponent or TLUDPComponent on a form. Assign OnAccept, OnError, OnReceive, OnConnect and OnDisconnect event handlers. Note that this is optional but in most<br />
cases required for functionality.<br />
<br />
Client connection is initialized with the Connect call. Arguments are IP address/hostname and port respectivly. Non-blocking connect is used, this means that you '''DON'T''' know that you're connected until OnConnect event is fired.<br />
<br />
Server connection is initialized with the Listen* call. Argument is port.<br />
<br />
{{Note| address is a string representation of IP eg: '127.0.0.1' or a hostname like 'localhost'.}}<br />
<br />
Sending data to the other side is accomplished with the Send/SendMessage method.<br />
<br />
===Events of TLNetComponent===<br />
<br />
* OnError - this event is fired whenever a connection error occurs. If no handler is assigned an excepion with "msg" is raised. Arguments are: msg - contains string and numerical representation of error message formatted in native encoding or UTF8 (depending on ForceUTF8 property). aSocket is the socket at which the error occured, nil means that the error is not a socket specific one.<br />
<br />
* OnConnect - This event is fired when the client succesfuly connects. aSocket is the socket which connected. It should serve as the "start" point for clients.<br />
<br />
* OnAccept - this event is fired whenever a connection is accepted on a server. If no handler is assigned nothing is done. aSocket contains the socket which accepted the connection.<br />
<br />
* OnDisconnect - this event is fired whenever a connection is lost. If no handler is assigned nothing is done. aSocket contains the socket which got disconnected.<br />
<br />
* OnReceive - This event is fired whenever new data is '''ready to be received'''. If no handler is assigned nothing is done, but a system buffer fill may happen which will in turn fire an OnError event. aSocket is the socket at which the data got received. Data is read by Get/GetMessage method.<br />
<br />
* OnCanSend - This event is fired whenever new data can be sent again on a socket, after a call to send function failed, returning 0. It can be used to automate sending of big chunks. aSocket is socket on which send can be called.<br />
<br />
== Documentation ==<br />
<br />
Documentation of lNet is partially done. You can also use Jesus' diagrams of classes and units as basic reference.<br />
<br />
Documentation of eventer and TCP/UDP protocols: http://members.chello.sk/ales/docs ([https://web.archive.org/web/20230000000000*/http://members.chello.sk/ales/docs Archived version at archive.org]}<br><br />
<br />
Overview diagrams can be found here: http://members.chello.sk/ales/docs/diagrams/overview.html<br><br />
Pictures: <br><br />
[http://members.chello.sk/ales/docs/diagrams/inheritance_composition.png New class diagram]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/allclasses.png All classes]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits.png lNet units]<br><br />
[http://members.chello.sk/ales/docs/diagrams/extra/lnetunits2.png lNet units 2]<br><br />
<br />
Docs Mirror and Ongoing Notes: http://z505.com/cgi-bin/powtils/docs/1.6/idx.cgi?file=lnetlufdoc<br />
<br />
== Installation ==<br />
LNet 0.5+:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetvisual.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| users who don't want to depend on LCL but use LNet in Lazarus (non-visually), can just compile lnetbase.lpk, which adds all the base units to path.}}<br />
<br />
LNet 0.4:<br />
* In the lazarus/components directory, unzip the files from lnet-<version>.zip file. The lnet-<version> folder will be created.<br />
* Open Lazarus<br />
* Open the package lnet-<version>/lazaruspackage/lnetpackage.lpk with Package/Open package file (.lpk)<br />
* Click on Compile<br />
* Open the package lnet-<version>/lazaruspackage/ide/lnetidepackage.lpk with Package/Open package file (.lpk)<br />
* Click on Use/Install and answer 'Yes' when you are asked about Lazarus rebuilding. A new tab named 'lNet' will be created in the components palette.<br />
{{Note| older lNet is extraced to "lnetpackage" dir. Since 0.4.0 the "visual" and "non visual" packages are in one distribution file.}}<br />
<br />
==Package contents==<br />
The package consists of base lNet units library, lTelnet for telnet protocol, lFTP for ftp protocol and lNetComponents libraries for providing visual and non-visual components for networking. As of 0.4.0 lHTTP and lSMTP components have been added. Since 0.5.1 there's a MIME support as part of the library mainly for creation of multipart messages for SMTP. Since 0.6.0 lNet supports SSL in a modular way, and SMTP protocol has been extended to full support of SSL/TLS (including runtime STARTTLS negotiation). HTTPS client side is supported too.<br />
<br />
==Testing==<br />
The non-visual console components were tested on Win32, Win64, Linux_x86_32, Linux_x86_64, Linux_PPC, Linux_PPC_64, Linux_ARM* (2.1.4+) and FreeBSD_x86_32.<br />
The visual (Lazarus packages) components were tested in Win32, Win64, ARM/WinCE, Linux_x86_32, Linux_x86_64 and FreeBSD_x86_32. (gtk1 and gtk2 on unix platforms)<br />
<br />
==History==<br />
<br />
* March 2011: A new version of lNet was just released. 0.6.5 completes SSL and IPv6 support including server-side and per-socket (for SSL) setting. SSL and IPv6 bugs were fixed as well as some remaining disconnect problems related to server bind errors and lingering sockets. DecodeURL bug introduced in 0.6.4 was also fixed.<br />
<br />
* May 2010: A new version of lNet was just released. 0.6.4 adds Qt4 support inside Lazarus (lazarus latest trunk needed, 0.9.30 should be 1st stable with it). There were also numerous fixes (http URL parsing, FTP problems) and one major bug fixed (WinCE compilation with FPC 2.4.0+). Grab it at SVN, packages will be uploaded ASAP.<br />
<br />
* February 2010: Well there’s a skip in dates. Anyways version 0.6.3 of lNet has been released, you can find it in the downloads section. There’s been various bugfixes and file renames to avoid FPC conflicts. I didn’t find the time to do the QT4 port as I wanted but it’s in the works now. Some SSL fixes were made as well.<br />
<br />
* May 2008: Where to begin? This is a HUGE critical bugfix release. There was about 5 big bugs fixed in SSL section, one logical oversight in Session support. OpenSSL unit was added so WinCE compiles again and SMTP and FTP got bugs fixed in regards to possible desynchronization. I'd urge everyone who uses 0.6.x to update ASAP, but some bugs are also from 0.5.x (specifically the SMTP and FTP event deadlock problem).<br />
<br />
* May 2008: It seems that due to an oversight in what's packaged with FPC and for which platform, lNet 0.6.x doesn't currently compile on WinCE due to missing OpenSSL unit. The unit is in fpc 2.2.0, but is not compiled for that platform yet (it is in 2.2.1+). I will add the unit to lNet and release 0.6.2 shortly, but until then, WinCE users can work around this by copying [http://svn.freepascal.org/svn/fpc/trunk/packages/openssl/src/openssl.pas this file] into their lnetdir/lib directory. (same will be done in 0.6.2 so don't worry)<br />
<br />
* April 2008: 0.6.1 has been released today. This release fixes an ugly SSL logical bug in regards to the mandatory requirements of Certificate Authority List file and Keyfile proprties of SSLSession. They are no longer needed for normal SSL to work. A minor change in HTTP and handling of SSLSession are also present.<br />
<br />
== Stability ==<br />
<br />
* Base lNet TCP/UDP - stable<br />
* Telnet Client - stable*<br />
* FTP Client - stable*<br />
* SMTP Client - stable<br />
* HTTP Client - stable<br />
* HTTP Server - experimental<br />
* MIME streams - stable<br />
* SSL support (client-side) - stable<br />
* SSL support (server-side) - stable (0.6.5+)<br />
* IPv6 support - stable (0.6.5+)<br />
<br />
<nowiki>*</nowiki> FTP and Telnet still need some additional features like authentication methods.<br />
<br />
On Linux, since you start using LNet, GDB can crash randomly (upon compilations) with diverse kind of reports. Setting debugger to Automatic in both lnetbase and lnetvisual package's options fixes the issue.<br />
<br />
== Roadmap ==<br />
Items that are <s>struck through</s> have been completed in the indicated version:<br />
* Stress testing [always]<br />
* <s>Addition of Telnet and FTP clients (experimental level) [0.3]</s> (done)<br />
* <s>Cementing of TCP/UDP API [0.3]</s> (done)<br />
<br />
* Addition of ICMP Ping/Traceroute protocol [na] (cancelled due to various reasons*)<br />
* <s>Addition of SMTP, HTTP components [0.4]</s> (done)<br />
* <s>Stabilization of Telnet and FTP clients [0.5]</s> (done)<br />
* <s>Addition of MIME streams [0.5.1]</s> (done)<br />
* Addition of Telnet and FTP servers (experimental level) [???]<br />
* <s>Addition of SSL [0.6+]</s> (done)<br />
* Addition of Carbon and/or Cocoa widget set support [0.7+]<br />
* <s>Addition of Qt4 widget set support [0.6.4+]</s> (done)<br />
* Cleanup of SSL/HTTP (so pure HTTP doesn't depend on TLSSLSocket) [0.6.x?]<br />
<br />
<nowiki>*</nowiki> PING requires raw sockets which are only accessible to root on unix/linux platforms. It was decided that lNet won't support this, users who need PING should use the ping and ping6 programs via TProcess.<br />
<br />
== Requirements ==<br />
LNet 0.6+:<br />
* Lazarus 0.9.24+ and FPC 2.2.0+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 7.10). FastCGI considered broken for now.<br />
<br />
LNet 0.5:<br />
* Lazarus 0.9.22+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.10) and FreeBSD 5.5. FastCGI considered broken for now. <b>WinCE requires Lazarus 0.9.23+ to compile!</b><br />
<br />
LNet 0.4:<br />
* Lazarus 0.9.18+ and FPC 2.0.4+<br />
* Status: Beta<br />
* Issues: Completely tested on Windows (WinXP), Linux (Ubuntu 6.06LTS) and FreeBSD 6.1. HTTP server has issues under high load with fastcgi usage. FastCGI works only in Linux for now.<br />
<br />
=== Ubuntu special notice ===<br />
<br />
Some "missing files" are, in fact, in the "/net" subfolder and are not found by the fpc compiler. Simply copy missing files from the subfolder (lnet.pas, tomwinsock.pas, lfunc.inc...) "/usr/lib/lazarus/components/lnetpackage/lnet" in the "/usr/lib/lazarus/components/lnetpackage" folder (adjust paths as necessary). Then complete the setup by following the installation procedure. (by Steph12358)<br />
<br />
== API changes ==<br />
<br />
'''0.1 to 0.2'''<br />
* There have been some API changes from 0.1 to 0.2. These were required and should improve the usability of packages. I know API changes are always a PITA but I'll try to keep them minimal from now on. Version 0.3 will be the first beta from which API will remain unchanged (only adding new stuff will be possible).<br />
* OnRecieve() changed to OnReceive()<br />
* Accept() changed to Listen()<br />
* All events' arguments changed. SocketNumber (int) got changed into aSocket(TLSocket) because of logical and speed reasons<br />
<br />
'''0.2 to 0.3'''<br />
* Added OnCanSend and OnConnect. Connect is now non-blocking.<br />
* Removed buffersize and maxmsgs. Buffersize is no longer required because no internal buffer is used.<br />
<br />
'''0.3 to 0.4'''<br />
* Lots of additions. Only "breakage" is that TLSocket.Port got changed into TLSocket.LocalPort and TLSocket.PeerPort for consistency reasons.<br />
<br />
'''0.4 to 0.5'''<br />
* We did some API cleanup which resulted in API breakage.<br />
* All "connection" classes now descend from TLComponent<br />
* TLSocket now has a .Creator which is a TLComponent property specifying the creator (eg: TLTcp), so you can see now which socket in an event belongs to which connection.<br />
* Added "Connect()" and "Listen()" parameterless calls to all connections. This is mostly to enable Lazarus visual style, so people can use .Host and .Port properties and repeatedly just call ".Connect()" for example.<br />
* Added "StatusSet" set to SMTP and FTP components. This enables you to monitor specific FTP or SMTP actions and their results with events OnSuccess and OnFailure. You set the events to be watched in the set, and get results in one of the callbacks.<br />
* <b>Changed ALL callbacks which didn't use TLSocket as sender to use it. This means all FTP, SMTP and HTTP events which had "Sender: TL<HigherType>" were reverted to basic Socket style callbacks!</b><br />
<br />
'''0.5.4- to 0.5.5+'''<br />
* Some minor API changes were made which shouldn't break anything but I list them to be on the safe side.<br />
* .Timeout property on eventers and connections was set to Integer (from DWord) to support -1 as "block until an event happens"<br />
<br />
'''0.5 to 0.6'''<br />
* No changes in public API, only additions.<br />
<br />
'''Note for 0.4.0''': In version 0.4.0, I forgot to move lHTTPSettings unit from lib to the http example. It was NOT supposed to be a part of the library, just the example. DON'T use this, it's not a "library", it's a part of the example (http server will get it's own project later)<br />
<br />
== lNet for FreeBSD, Linux, macOS and Windows ==<br />
<br />
* [https://github.com/trevoz/lnet lNet for FreeBSD, Linux, macOS and Windows] <br />
** Many changes so that the library will now compile on the above with FPC 3.2.2 or FPC 3.3.1.<br />
** TLSv1.3 added<br />
** Linux TLS is not working due to an epoll problem in the file <tt>lib/sys/lepolleventer.inc</tt><br />
** The console examples compile (I have tested the SMTP example which was what I was after). <br />
** The visual component examples do not compile for macOS - there are no Cocoa, or even defunct Carbon, interface bindings.<br />
<br />
== See also ==<br />
<br />
* [[lNet examples]]<br />
<br />
[[Category:Components]]<br />
[[Category:Networking]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=157252Autosize / Layout/ru2023-09-05T14:04:02Z<p>Zoltanleo: /* Выравнивание (Aligned) */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.<br />
<br />
Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent родительского элемента управления управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера в RunTime является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 px, а widgetset в ответ изменит размер к 800 px. Если вы устанавливаете свойство ''Witdh'' в событии формы ''OnResize'', то можете создать бесконечное зацикливание. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что действие свойства ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся текущие границы окна. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как "прилипающие" края, которые изменяют размеры текущего окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое значение свойства ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" (очевидно, имеется ввиду ''Form1.AutoSize=True'') вам может потребоваться получить размер формы, прежде чем показывать ее. Для установки автомасштаба требуется дескриптор (хэндл окна). Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер окна. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchoring)=<br />
<br />
Элементы управления для привязки к четырем сторонам имеют свойство ''Anchors'' с возможными значениями: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' между привязанными элементами управления. По умолчанию значение свойства ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не может быть изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не может быть изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено свойство AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается в размере, расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений , сохраненных в файле формы lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя значение ''Left'' кнопки, увеличивая расстояние от правой стороны кнопки до правого края.<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние до правого края.<br />
<br />
'''Объяснение:''' установка значения '''Width''' кнопки эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему значение '''Left''' сохраняется. Это совместимо с поведением контролов в Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка значения ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние от ее правой границы до края компонента-родителя и изменив значение ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает, что:<br />
*вы можете привязать левый край label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing(зазор) управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, которая добавляется к каждому значению Left, Top, Right, Bottom элемента управления.<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот параметр используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*Значение Around добавляется к зазору для Left,Top,Right,Bottom элемента управления.<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют им растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на общем родителе) он максимален, если:<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на общем родителе) он максимален, если:<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни свойство Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к наличию зазора в 6px между Label'ом и Edit'ом и наличию зазора в 6px слева от Label'а, а также наличию зазора в 6px справа от Edit'а. Также еще существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, когда<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, когда<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все остальные зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет присутствовать зазор в 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который от Memo1 до Memo2 составит 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется зазор в 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через значения свойств ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все упомянутые значения равны 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align(выравнивание) работает почти так же, как в Delphi, и может использоваться для быстрого заполнения какой-нибудь области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия друг друга. Это означает, что все элементы управления свойства Align со значением ''alLeft, alTop, alBottom, alRight'' не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью значения свойства alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина всех элементов остается максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и значение свойства Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью значения свойства alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью значения свойства alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью значения свойства alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с установленным свойством alClient, он заполнит оставшуюся часть клиентской области.<br />
*Если имеется более одного элемента управления с установленным свойством alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них будет показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина свойства BorderSpacing и свойства ChildSize родителя применяется к выравниваемым элементам управления. Элемент управления memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять значение своей '''Width'''. Если привязка установлена, то значение '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный посредством alLeft или alRight, растягивается по вертикали и будет использовать свою ширину, определенную на этапе проектирования формы. Если свойство ''AutoSize'' установлено в ''true'', то значение параметра ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный посредством alClient, заполняет оставшееся пространство. Родительский элемент управления со значением свойства ''AutoSize=true'' при этом будет растягиваться или сжиматься так, чтобы впритирку умещать на себе свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления с установленным значением alCustom не перемещаются LCL, но могут быть перемещены вашим пользовательским элементом управления. Для этого вы должны переопределить методы <code>CalculatePreferredSize</code> и <code>DoAutoSize</code>.<br />
<br />
==Порядок расположения элементов управления с одинаковым значением Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим значением Left, для alTop - наименьшим значением Top, для alRight - наибольшая величина Left+Width, для alBottom - наибольшая величина Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до версии Lazarus 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию элементов управления и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с выравниванием alTop или alLeft без указания границ, все элементы управления будут помещены в координаты 0.0, и, следовательно, последние добавленные окажутся поверх контролов, добавленных ранее.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=157250Autosize / Layout/ru2023-09-05T13:56:30Z<p>Zoltanleo: /* Изменение размера родителя с привязанным контролом */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.<br />
<br />
Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent родительского элемента управления управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера в RunTime является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 px, а widgetset в ответ изменит размер к 800 px. Если вы устанавливаете свойство ''Witdh'' в событии формы ''OnResize'', то можете создать бесконечное зацикливание. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что действие свойства ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся текущие границы окна. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как "прилипающие" края, которые изменяют размеры текущего окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое значение свойства ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" (очевидно, имеется ввиду ''Form1.AutoSize=True'') вам может потребоваться получить размер формы, прежде чем показывать ее. Для установки автомасштаба требуется дескриптор (хэндл окна). Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер окна. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchoring)=<br />
<br />
Элементы управления для привязки к четырем сторонам имеют свойство ''Anchors'' с возможными значениями: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' между привязанными элементами управления. По умолчанию значение свойства ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не может быть изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не может быть изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено свойство AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается в размере, расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений , сохраненных в файле формы lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя значение ''Left'' кнопки, увеличивая расстояние от правой стороны кнопки до правого края.<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние до правого края.<br />
<br />
'''Объяснение:''' установка значения '''Width''' кнопки эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему значение '''Left''' сохраняется. Это совместимо с поведением контролов в Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка значения ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние от ее правой границы до края компонента-родителя и изменив значение ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает, что:<br />
*вы можете привязать левый край label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing(зазор) управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, которая добавляется к каждому значению Left, Top, Right, Bottom элемента управления.<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот параметр используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*Значение Around добавляется к зазору для Left,Top,Right,Bottom элемента управления.<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют им растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на общем родителе) он максимален, если:<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на общем родителе) он максимален, если:<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни свойство Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к наличию зазора в 6px между Label'ом и Edit'ом и наличию зазора в 6px слева от Label'а, а также наличию зазора в 6px справа от Edit'а. Также еще существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, когда<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, когда<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все остальные зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет присутствовать зазор в 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который от Memo1 до Memo2 составит 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется зазор в 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через значения свойств ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все упомянутые значения равны 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=157249Autosize / Layout/ru2023-09-05T13:55:39Z<p>Zoltanleo: /* Привязка сторон (Anchored) */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.<br />
<br />
Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent родительского элемента управления управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера в RunTime является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 px, а widgetset в ответ изменит размер к 800 px. Если вы устанавливаете свойство ''Witdh'' в событии формы ''OnResize'', то можете создать бесконечное зацикливание. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что действие свойства ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся текущие границы окна. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как "прилипающие" края, которые изменяют размеры текущего окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое значение свойства ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" (очевидно, имеется ввиду ''Form1.AutoSize=True'') вам может потребоваться получить размер формы, прежде чем показывать ее. Для установки автомасштаба требуется дескриптор (хэндл окна). Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер окна. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchoring)=<br />
<br />
Элементы управления для привязки к четырем сторонам имеют свойство ''Anchors'' с возможными значениями: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' между привязанными элементами управления. По умолчанию значение свойства ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не может быть изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не может быть изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено свойство AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается в размере, расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя значение ''Left'' кнопки, увеличивая расстояние от правой стороны кнопки до правого края.<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние до правого края.<br />
<br />
'''Объяснение:''' установка значения '''Width''' кнопки эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему значение '''Left''' сохраняется. Это совместимо с поведением контролов в Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка значения ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние от ее правой границы до края компонента-родителя и изменив значение ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает, что:<br />
*вы можете привязать левый край label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing(зазор) управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, которая добавляется к каждому значению Left, Top, Right, Bottom элемента управления.<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот параметр используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*Значение Around добавляется к зазору для Left,Top,Right,Bottom элемента управления.<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют им растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на общем родителе) он максимален, если:<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на общем родителе) он максимален, если:<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни свойство Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к наличию зазора в 6px между Label'ом и Edit'ом и наличию зазора в 6px слева от Label'а, а также наличию зазора в 6px справа от Edit'а. Также еще существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, когда<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, когда<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все остальные зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет присутствовать зазор в 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который от Memo1 до Memo2 составит 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется зазор в 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через значения свойств ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все упомянутые значения равны 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=157248Autosize / Layout/ru2023-09-05T13:50:37Z<p>Zoltanleo: /* Автомасштаб и формы */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.<br />
<br />
Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent родительского элемента управления управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера в RunTime является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 px, а widgetset в ответ изменит размер к 800 px. Если вы устанавливаете свойство ''Witdh'' в событии формы ''OnResize'', то можете создать бесконечное зацикливание. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что действие свойства ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся текущие границы окна. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как "прилипающие" края, которые изменяют размеры текущего окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое значение свойства ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" (очевидно, имеется ввиду ''Form1.AutoSize=True'') вам может потребоваться получить размер формы, прежде чем показывать ее. Для установки автомасштаба требуется дескриптор (хэндл окна). Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер окна. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=157247Autosize / Layout/ru2023-09-05T13:47:55Z<p>Zoltanleo: /* Автомасштаб и перемещение дочерних элементов управления */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.переводчика]]: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.<br />
<br />
Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=157246Autosize / Layout/ru2023-09-05T13:46:36Z<p>Zoltanleo: /* Автомасштаб и перемещение дочерних элементов управления */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
--[[User:Zoltanleo|Прим.перводчика]]: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.<br />
<br />
Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Executing_External_Programs/ru&diff=156646Executing External Programs/ru2023-06-19T14:22:08Z<p>Zoltanleo: /* Использование ShellExecuteEx с повышенными (правами администратора) правами */</p>
<hr />
<div>{{Executing External Programs}}<br />
<br />
== Введение: сравнение==<br />
<br />
В библиотеках RTL, FCL, LCL есть разные способы выполнить внешнюю программу.<br />
{| class="wikitable"<br />
!Метод<br />
!Библиотека<br />
!Платформы<br />
!Одной строкой?<br />
!Особенности<br />
|-<br />
|[[Executing External Programs/ru#SysUtils.ExecuteProcess|ExecuteProcess]]<br />
|RTL<br />
|кроссплатформенный<br />
|Да<br />
|Очень ограничен, синхронен<br />
|-<br />
|[[Executing External Programs/ru#MS Windows : CreateProcess, ShellExecute и WinExec|ShellExecute]]<br />
|WinAPI<br />
|Только MS Windows<br />
|Да<br />
|Много. Может запускать программы с правами администратора<br />
|-<br />
|[[Executing External Programs/ru#Unix fpsystem, fpexecve и shell |fpsystem, fpexecve]]<br />
|Unix<br />
|Unix only<br />
|<br />
|<br />
|-<br />
|[[Executing External Programs/ru#TProcess|TProcess]]<br />
|FCL<br />
|кроссплатформенный<br />
|Нет<br />
|Все<br />
|-<br />
|[[Executing External Programs/ru#(Process.)RunCommand|RunCommand]]<br />
|FCL<br />
|кроссплатформенный '''Требуется FPC 2.6.2+'''<br />
|Да<br />
|Покрывает стандартное использование TProcess<br />
|-<br />
|[[Executing External Programs/ru#Альтернативные решения с помощью LCLIntf|OpenDocument]]<br />
|LCL<br />
|кроссплатформенный<br />
|Да<br />
|Только открывает документ. Документ будет открыт стандартной программой<br />
|}<br />
<br />
Если вы использовали '''ShellExecute''' и/или '''WinExec''' в Delphi, то вы можете начать использовать TProcess как альтернативу в FPC/Lazarus (это верно и в случае использования Lazarus на Linux, потому что TProcess является кроссплатформенным компонентом).<br />
<br />
{{Note| FPC/Lazarus поддерживает '''ShellExecute''' и '''WinExec''', но только в среде Win32. Если вы пишете кросс-платформенную программу, то лучшим путем будет использование TProcess!}}<br />
<br />
==(Process.)RunCommand==<br />
* [http://www.freepascal.org/docs-html/fcl/process/runcommand.html RunCommand] документация<br />
* [http://www.freepascal.org/docs-html/fcl/process/runcommandindir.html RunCommandInDir] докуметация<br />
<br />
В FPC 2.6.2, некоторые вспомогательные функции для TProcess были добавлены в модуль process основанный на обертке использованной в [[Projects using Lazarus#fpcup|fpcup]].<br />
Эти функции могут быть для базового и среднего уровня использования и могот захватить вывод как одной строки, так и ''огромного количества'' выводимой информации. <br />
<br />
Простой пример:<br />
<syntaxhighlight lang=pascal><br />
uses Process;<br />
...<br />
var s : ansistring;<br />
...<br />
if RunCommand('/bin/bash',['-c','echo $PATH'],s) then<br />
writeln(s); <br />
</syntaxhighlight> <br />
Перегруженный вариант функции может возвращать код выхода программы. RunCommandInDir запускает программу в заданной папке.<br />
<syntaxhighlight lang=pascal><br />
function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;<br />
function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;<br />
function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;<br />
</syntaxhighlight><br />
<br />
== SysUtils.ExecuteProcess ==<br />
<br />
Простейший путь, если вам не нужно общение с процессом - это просто использовать вызов<br />
<syntaxhighlight lang=pascal><br />
SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2']);<br />
</syntaxhighlight><br />
При использовании этого метода, приложение "повисает" до тех пор, пока вызванная программа не завершится. Это может быть полезно если пользователь должен сделать что-то перед использованием вашего приложения. Если вы хотите обойти это ограничение, то более полезный кроссплатформенный способ - это '''TProcess''', или если ваша целевая платформа только Windows, то используйте '''ShellExecute'''.<br />
<br />
== MS Windows : CreateProcess, ShellExecute и WinExec ==<br />
<br />
{{Note|FPC/Lazarus поддерживают '''CreateProcess''', '''ShellExecute''' и/или '''WinExec''', только на Win32/64. Если ваша программа кроссплатформенная, используйте '''RunCommand''' или '''TProcess'''.}}<br />
{{Note|WinExec использует 16-битные вызовы и давно устарел. Новые версии FPC при его использовании генерируют предупреждение.}}<br />
<br />
'''ShellExecute''' - стандартная функция MS Windows (ShellApi.h) с хорошей [http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx документацией на MSDN] (читайте документацию, если вам кажется, что функция ненадежна).<br />
<br />
<syntaxhighlight lang=pascal><br />
uses ..., ShellApi;<br />
<br />
// Обычный текст (возвращаемые ошибки игнорируются) :<br />
if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) = 0 then;<br />
<br />
// Выполняем Batch файл:<br />
if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) = 0 then;<br />
<br />
// Открываем командную строку в заданной папке:<br />
if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) = 0 then;<br />
<br />
// Открываем веб-страницу стандартным браузером с помощью'start' (через скрытую командную строку) :<br />
if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;<br />
<br />
// или полезную команду:<br />
procedure RunShellExecute(const prog,params:string);<br />
begin<br />
// ( Handle, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' не всегда требуется <br />
// path+prog, params, working folder,<br />
// 0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min) // for SW_ constants : uses ... Windows ...<br />
if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //успех<br />
// если возвращается число в диапазоне 0..32, то значит ошибка<br />
end;<br />
</syntaxhighlight><br />
<br />
Также есть WideChar функции - ShellExecuteExW , ShellExecuteExA в режиме AnsiChar.<br />
<br />
В качестве fMask можно ипользовать SEE_MASK_DOENVSUBST, SEE_MASK_FLAG_NO_UI или SEE_MASK_NOCLOSEPROCESS и тд<br />
Если в Delphi вы используете ShellExecute для открытия документов, например документ Word или ссылки, то присмотритесь к open* (OpenURL и т.д.) модуле lclintf.<br />
<br />
=== Использование ShellExecuteEx с повышенными правами (администратора)===<br />
Если вы хотите использовать программу с повышенными (правами администратора) правами, используйте '''runas''' как альтернативу ShellExecuteEx:<br />
<syntaxhighlight lang=pascal><br />
uses ShellApi, ...;<br />
<br />
function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;<br />
var<br />
sei: TShellExecuteInfoA;<br />
begin<br />
FillChar(sei, SizeOf(sei), 0);<br />
sei.cbSize := SizeOf(sei);<br />
sei.Wnd := Handle;<br />
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;<br />
sei.lpVerb := 'runas';<br />
sei.lpFile := PAnsiChar(Path);<br />
sei.lpParameters := PAnsiChar(Params);<br />
sei.nShow := SW_SHOWNORMAL;<br />
Result := ShellExecuteExA(@sei);<br />
end;<br />
<br />
procedure TFormMain.RunAddOrRemoveApplication;<br />
begin<br />
// Пример, открывающий панель управления с повышенными правами<br />
RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');<br />
end;<br />
</syntaxhighlight><br />
<br />
== Unix fpsystem, fpexecve и shell ==<br />
<br />
Эти функции являются платформозависимыми.<br />
<br />
* [http://www.freepascal.org/docs-html/rtl/unix/fpsystem.html справка по fpsystem]<br />
* [http://www.freepascal.org/docs-html/rtl/baseunix/fpexecve.html справка по fpexecve]<br />
* [http://www.freepascal.org/docs-html/rtl/unix/shell.html справка по shell]<br />
<br />
Учтите, что '''Unix.Shell''' версии 1.0.x устарел и удален из trunk. Используйте fpsystem.<br />
<br />
== TProcess ==<br />
Вы можете использовать TProcess для запуска внешних программ. Самыми полезными вещами при этом будут:<br />
<br />
*Платформонезависимость<br />
*Способность читать из stdout и писать в stdin.<br />
<br />
{{Note| TProcess не оболочка! И не терминал! Вы не можете напрямую исполнять скрипты или перенаправлять вывод используя такие операторы, как "|", ">", "<", "&" и т.д. Но возможно получить те же самые результаты используя TProcess, далее будут приведены некоторые примеры.}}<br />
<br />
Важно: Вы должны определять полный путь к исполняемому файлу. Например '/bin/cp' вместо 'cp'. Если программа находится где-либо в переменной PATH, то вы можете использовать функцию [https://lazarus-ccr.sourceforge.io/docs/lazutils/fileutil/finddefaultexecutablepath.html FindDefaultExecutablePath] из модуля LCL [https://lazarus-ccr.sourceforge.io/docs/lazutils/fileutil/index.html FileUtil].<br />
<br />
=== Простейший пример ===<br />
<br />
Многие типичные случаи были подготовлены в функциях [[Executing_External_Programs#.28Process..29RunCommand|Runcommand]]. Прежде, чем начать копировать и вставлять приведенные ниже примеры, сначала проверьте их.<br />
<br />
=== Простой пример ===<br />
<syntaxhighlight lang=pascal><br />
// Демо-программа, показывающая, как можно запустить<br />
// внешнюю программу<br />
program launchprogram;<br />
<br />
// Подключаем модули с требуемыми<br />
// нам процедурами и функциями.<br />
uses <br />
Classes, SysUtils, Process;<br />
<br />
// Опишем переменную "AProcess" <br />
// типа "TProcess"<br />
var <br />
AProcess: TProcess;<br />
<br />
// Здесь наша программа начинается<br />
begin<br />
// Создаем объект TProcess и<br />
// присваиваем его переменной AProcess.<br />
AProcess := TProcess.Create(nil);<br />
<br />
// Сообщаем новому AProcess, что это за команда для выполнения.<br />
// Давайте использовать компилятор Free Pascal (версия i386)<br />
AProcess.Executable:= 'ppc386';<br />
<br />
// Передаеv -h вместе с ppc386, чтобы фактически выполнить 'ppc386 -h':<br />
AProcess.Parameters.Add('-h');<br />
<br />
// Мы определим опцию, когда программа <br />
// будет запущена. Эта опция гарантирует, что наша программа <br />
// не продолжит работу, пока программа, которую мы запустили, <br />
// не перестанет работать. vvvvvvvvvvvvvv<br />
AProcess.Options := AProcess.Options + [poWaitOnExit];<br />
<br />
// Теперь пусть Process запустит программу<br />
AProcess.Execute;<br />
<br />
// Пока ppc386 не прекратит работу, мы досюда не дойдем<br />
AProcess.Free; <br />
end.<br />
</syntaxhighlight><br />
<br />
Вот оно! Теперь вы научились запускать внешнюю программу изнутри вашей собственной.<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: на момент правки статьи (fpc 3.0.4) свойство ''CommandLine'' (как и ''ApplicationName'') согласно [https://www.freepascal.org/docs-html/current/fcl/process/tprocess.html документации ] объявлены '''deprecated'''. Вместо них рекомендуется использовать свойства [https://www.freepascal.org/docs-html/current/fcl/process/tprocess.parameters.html Parameters] и [https://www.freepascal.org/docs-html/current/fcl/process/tprocess.executable.html Executable] соответственно.<br />
<br />
{{Note| для пользователей Windows - если путь к исполняемому файлу включает символы пробела, табуляции или перевода строки, то он должен заключаться в двойные кавычки (например, "c:\Program Files\Git\bin\git.exe"). Это же правило справедливо в случае, если такой путь указывается в аргументах командной строки (в свойстве Parameters).}}<br />
<br />
{{Note| строковый агрумент, добавляемый в Parameters, не должен содержать пробелы.}}<br />
<syntaxhighlight lang=pascal><br />
...<br />
// к примеру, один из аргументов командной строки должен быть '-user sysdba'<br />
//тогда он должен быть записан так<br />
AProcess.Parameters.Add('-user');<br />
AProcess.Parameters.Add('sysdba');<br />
...<br />
</syntaxhighlight><br />
----<br />
<br />
=== Усовершенствованный пример (но пока не правильный) ===<br />
Это все замечательно, но как я могу получить вывод программы, которую я запустил?<br />
Что ж, давайте немного расширим наш пример и сделаем так: '''этот пример прост, поэтому вы можете извлечь из него уроки. Пожалуйста, не используйте этот пример в рабочем коде, а используйте код из раздела''' [[Executing_External_Programs/ru#.D0.A7.D1.82.D0.B5.D0.BD.D0.B8.D0.B5_.D0.B1.D0.BE.D0.BB.D1.8C.D1.88.D0.B8.D1.85_.D0.BE.D0.B1.D1.8A.D0.B5.D0.BC.D0.BE.D0.B2_.D0.B2.D1.8B.D0.B2.D0.BE.D0.B4.D0.B0|Чтение больших объемов вывода]]<br />
<br />
<syntaxhighlight lang="pascal"><br />
// Это <br />
// ДЕФЕКТНАЯ<br />
// демонстрационная программа, которая показывает<br />
// как запустить внешнюю программу<br />
// и читать из ее вывода.<br />
program launchprogram;<br />
<br />
// Здесь мы включаем файлы, которые имеют полезные функции<br />
// и процедуры, и которые нам понадобятся.<br />
uses <br />
Classes, SysUtils, Process;<br />
<br />
// Это определение переменной «AProcess», как переменной<br />
// типа "TProcess".<br />
// Также еще мы добавляем TStringList для хранения<br />
// данных, читаемых из вывода программ.<br />
var <br />
AProcess: TProcess;<br />
AStringList: TStringList;<br />
<br />
// Это место, где наша программа начинает работать<br />
begin<br />
// Теперь мы создадим объект Process и <br />
// назначим его переменной var AProcess.<br />
AProcess := TProcess.Create(nil);<br />
<br />
// Сообщим новому AProcess, что это за команда для выполнения.<br />
AProcess.Executable := '/usr/bin/ppc386'; <br />
AProcess.Parameters.Add('-h'); <br />
<br />
// Мы определим опцию, когда программа<br />
// выполняется. Эта опция гарантирует, что наш AProcess<br />
// не будет продолжаться до тех пор, пока запущенная нами программа<br />
// остановится. Также теперь мы сообщим AProcess, что<br />
// мы хотим прочитать вывод файла.<br />
AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];<br />
<br />
// Теперь, когда AProcess знает, что это за командная строка, его можно запустить.<br />
AProcess.Execute;<br />
<br />
// После завершения AProcess, остальная часть программы будет выполнена.<br />
<br />
// Теперь читаем вывод программы, который мы только что запихали в TStringList.<br />
AStringList := TStringList.Create;<br />
AStringList.LoadFromStream(AProcess.Output);<br />
<br />
// Сохраняем выходные данные в файл и очищаем TStringList.<br />
AStringList.SaveToFile('output.txt');<br />
AStringList.Free;<br />
<br />
// Теперь, когда выходные данные процесса обработаны, его можно освободить.<br />
AProcess.Free; <br />
end.</syntaxhighlight><br />
<br />
=== Чтение больших объемов вывода ===<br />
В предыдущем примере мы ждали завершения запущенной программы. После этого мы считывали все, что программа записала в выходной поток. Но ведь может оказаться и так, что программа выведет много данных, канал заполнится и вывод остановится, при этом запустившая программа ждет завершения запущенной программы, которая в свою очередь не может завершить работу, пока не выведет все данные. Возникает коллизия, dead-lock.<br />
<br />
Поэтому следующий пример не будет использовать опцию poWaitOnExit и будет читать данные при запущенной программе. Вывод записывается в поток, который можно позже использовать для чтения его содержимого в TStringList.<br />
<br />
Если вы хотите считывать выходные данные из внешнего процесса, вы должны адаптировать этот код для повседневного использования.<br />
<br />
<syntaxhighlight lang=pascal> program LargeOutputDemo;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
Classes, Process, SysUtils; // Process - модуль, содержащий TProcess<br />
<br />
const<br />
BUF_SIZE = 2048; // Размер буфера для чтения выходных данных блоками<br />
var<br />
AProcess : TProcess;<br />
OutputStream : TStream;<br />
BytesRead : longint;<br />
Buffer : array[1..BUF_SIZE] of byte;<br />
<br />
begin<br />
// Установки процесса; в качестве примера используется рекурсивный поиск в каталоге,<br />
// потому что это обычно приводит к большому количеству данных.<br />
AProcess := TProcess.Create(nil);<br />
<br />
//Команды для Windows и *nix отличаются, поэтому используется директива $IFDEFs<br />
{$IFDEF Windows}<br />
// В Windows команду dir нельзя использовать напрямую, потому что она является встроенной<br />
// командой оболочки. Поэтому нужны cmd.exe и дополнительные параметры<br />
AProcess.Executable := 'c:\windows\system32\cmd.exe';<br />
AProcess.Parameters.Add('/c');<br />
AProcess.Parameters.Add('dir /s c:\windows');<br />
{$ENDIF Windows}<br />
<br />
{$IFDEF Unix}<br />
AProcess.Executable := '/bin/ls';<br />
AProcess.Parameters.Add('--recursive');<br />
AProcess.Parameters.Add('--all');<br />
AProcess.Parameters.Add('-l');<br />
{$ENDIF Unix}<br />
<br />
// Необходимо использовать poUsePipes - параметр TProcess, чтобы можно было получить выходные данные.<br />
// Параметр TProcess'а poWaitOnExit не может быть использован, потому что он заблокирует<br />
// эту программу до окончания вызванного процесса, препятствуя чтению выходных данных процесса.<br />
AProcess.Options := [poUsePipes];<br />
<br />
//Запуск процесса (выполнение команды dir/ls)<br />
AProcess.Execute;<br />
<br />
// Создаем объект потока для хранения сгенерированного вывода. Вывод может<br />
// также являться файловым потоком для непосредственного сохранения выводимых данных на диск.<br />
OutputStream := TMemoryStream.Create;<br />
<br />
// Все сгенерированные выходные данные из AProcess читаются в цикле, пока больше не останется данных<br />
repeat<br />
// Получаем новые данные из процесса до максимального размера выделенного буфера.<br />
// Обратите внимание, что все вызовы read (...) будут блокироваться, кроме последнего, который возвращает 0 (ноль).<br />
BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);<br />
<br />
// Добавляем байты, которые были прочитаны в поток для последующего использования<br />
OutputStream.Write(Buffer, BytesRead)<br />
<br />
until BytesRead = 0; // Останавливаемся, если больше нет данных<br />
<br />
// Процесс завершен, поэтому его можно очистить<br />
AProcess.Free;<br />
<br />
//Теперь, когда все данные прочитаны, их можно использовать; например, чтобы сохранить их в файл на диске<br />
with TFileStream.Create('output.txt', fmCreate) do<br />
begin<br />
OutputStream.Position := 0; // Требуется, чтобы убедиться, что все данные копируются с самого начала<br />
CopyFrom(OutputStream, OutputStream.Size);<br />
Free<br />
end;<br />
<br />
// Или данные могут быть показаны на экране<br />
with TStringList.Create do<br />
begin<br />
OutputStream.Position := 0; // Требуется, чтобы убедиться, что все данные копируются с самого начала<br />
LoadFromStream(OutputStream);<br />
writeln(Text);<br />
writeln('--- Номер строки = ', Count, '----');<br />
Free<br />
end;<br />
<br />
// Очищаем<br />
OutputStream.Free;<br />
end.</syntaxhighlight><br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: можно выводить данные сразу в какой-нибудь memoOutput на основе ([https://forum.lazarus.freepascal.org/index.php/topic,17315.msg95228.html#msg95228 решения], предложенного пользователем [https://forum.lazarus.freepascal.org/index.php?action=profile;u=47822 KpjComp]):<br />
<syntaxhighlight lang=pascal><br />
const<br />
BUF_SIZE = 1024 * 64; // Размер буфера для чтения выходных данных, можно читать блоками по 64k<br />
var<br />
Buffer: string = '';<br />
BytesRead: LongInt = 0; <br />
begin<br />
...<br />
//Запуск процесса (выполнение команды dir/ls)<br />
AProcess.Execute;<br />
<br />
//можно дать чуть-чуть времени на отправку данных через AProcess.Output <br />
sleep(500);<br />
<br />
repeat<br />
memoOutput.Lines.BeginUpdate;<br />
try<br />
SetLength(Buffer,BUF_SIZE);<br />
BytesRead := AProcess.Output.Read(Buffer[1],Length(Buffer));<br />
if BytesRead > 0 then<br />
begin<br />
SetLength(Buffer,BytesRead);<br />
memoOutput.Append(Buffer);<br />
end;<br />
finally<br />
memoOutput.Lines.EndUpdate;<br />
memoOutput.SelStart := UTF8Length(memoOutput.Text);<br />
end;<br />
Sleep(50);<br />
Application.ProcessMessages;<br />
until BytesRead = 0;</syntaxhighlight><br />
<br />
----<br />
<br />
Обратите внимание, что вышеперечисленное также может быть достигнуто с использованием RunCommand:<br />
<br />
<syntaxhighlight lang="pascal"><br />
var s: string;<br />
...<br />
RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);</syntaxhighlight><br />
<br />
=== Использование ввода и вывода TProcess ===<br />
Смотри демо пример на [https://sourceforge.net/p/lazarus-ccr/svn/913/tree/examples/process Lazarus-CCR SVN].<br />
<br />
=== Некоторые подсказки при использовании TProcess ===<br />
Если вы создаете кроссплатформенную программу, вы можете изменять командную строку применительно к каждой ОС использую директивы "{$IFDEF}" и "{$ENDIF}".<br />
<br />
Например:<br />
<syntaxhighlight lang=pascal> {...}<br />
AProcess:TProcess.Create(nil)<br />
{$IFDEF WIN32}<br />
AProcess.CommandLine := 'calc.exe'; // Windows калькулятор<br />
{$ENDIF}<br />
{$IFDEF LINUX}<br />
AProcess.CommandLine := 'kcalc'; // KDE калькулятор<br />
{$ENDIF}<br />
AProcess.Execute; // как альтернативу, вы можете использовать AProcess.Active:=True<br />
{...}</syntaxhighlight><br />
<br />
=== Показ комплекта приложений на переднем плане в macOS ===<br />
<br />
Вы можете запустить '''application bundle'''(пакет приложения) через TProcess, вызвав исполняемый файл внутри пакета. Например:<br />
<syntaxhighlight lang=pascal><br />
AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';<br />
</syntaxhighlight><br />
<br />
Это запустит ''Calendar'', но окно будет позади текущего приложения.<br />
Чтобы получить приложение на переднем плане, вы можете использовать утилиту '''open''' с параметром '''-n''':<br />
<syntaxhighlight lang=pascal><br />
AProcess.Executable:='/usr/bin/open';<br />
AProcess.Parameters.Add('-n');<br />
AProcess.Parameters.Add('-a'); //необязательно: скрыть терминал - аналогично опции Windows poNoConsole<br />
AProcess.Parameters.Add('/Application/iCal.app');<br />
</syntaxhighlight><br />
<br />
Если вашему приложению нужны параметры, вы можете передать '''open''' параметр '''--args''', после чего все параметры будут переданы приложению:<br />
<syntaxhighlight lang=pascal><br />
AProcess.Parameters.Add('--args');<br />
AProcess.Parameters.Add('argument1');<br />
AProcess.Parameters.Add('argument2');<br />
</syntaxhighlight><br />
<br />
=== Запуск отдельной программы ===<br />
<br />
Обычно программа, запускаемая вашим приложением, является дочерним процессом и уничтожается, когда ваше приложение уничтожается. Если вы хотите запустить автономную программу, которая продолжает работать [после завершения вашего приложения], вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang=pascal><br />
var<br />
Process: TProcess;<br />
I: Integer;<br />
begin<br />
Process := TProcess.Create(nil);<br />
try<br />
Process.InheritHandles := False;<br />
Process.Options := [];<br />
Process.ShowWindow := swoShow;<br />
<br />
// Копируем переменные среды по умолчанию, включая переменную DISPLAY, для работы приложения с графическим интерфейсом<br />
for I := 1 to GetEnvironmentVariableCount do<br />
Process.Environment.Add(GetEnvironmentString(I));<br />
<br />
Process.Executable := '/usr/bin/gedit'; <br />
Process.Execute;<br />
finally<br />
Process.Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Пример "общения" с процессом aspell ===<br />
<br />
Внутри исходного кода [https://github.com/pasdoc/pasdoc/wiki pasdoc] вы можете найти два модуля, которые выполняют проверку орфографии, "общаясь" с запущенным процессом aspell через каналы:<br />
<br />
* Модуль [https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_ProcessLineTalk.pas PasDoc_ProcessLineTalk.pas] реализует класс TProcessLineTalk, потомок TProcess, который может быть легко использован для общения с любым процессом построчно.<br />
<br />
* Модуль [https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas] реализует класс TAspellProcess, который выполняет проверку орфографии с использованием базового экземпляра TProcessLineTalk для выполнения aspell и связи с запущенным процессом aspell.<br />
<br />
Оба модуля довольно независимы от остальных исходников pasdoc, поэтому они могут служить реальными примерами использования TProcess для запуска и связи по каналам с другой программой.<br />
<br />
=== Замена операторов командной оболочки, таких как "| < >" ===<br />
<br />
Иногда у вас есть желание выполнить более сложную команду, которая передает свои данные другой команде или файлу.<br />
Что-то вроде<br />
<syntaxhighlight lang=bash>ShellExecute('firstcommand.exe | secondcommand.exe');</syntaxhighlight><br />
или<br />
<syntaxhighlight lang=bash>ShellExecute('dir > output.txt');</syntaxhighlight><br />
<br />
Запуск таких команд с TProcess не будет работать. То есть:<br />
<br />
<syntaxhighlight lang=pascal>// так работать не будет<br />
Process.CommandLine := 'firstcommand.exe | secondcommand.exe'; <br />
Process.Execute;</syntaxhighlight><br />
<br />
==== Почему использование специальных операторов для перенаправления вывода не работает ====<br />
TProcess - это не оболочка, а процесс. Это не два процесса, а всего один. Можно перенаправить вывод так, как вы этого пожелаете. См. [[Executing_External_Programs/ru#.D0.9A.D0.B0.D0.BA_.D0.BF.D0.B5.D1.80.D0.B5.D0.BD.D0.B0.D0.BF.D1.80.D0.B0.D0.B2.D0.B8.D1.82.D1.8C_.D0.B2.D1.8B.D0.B2.D0.BE.D0.B4_.D1.81_.D0.BF.D0.BE.D0.BC.D0.BE.D1.89.D1.8C.D1.8E_TProcess|следующий раздел]].<br />
<br />
=== Как перенаправить вывод с помощью TProcess ===<br />
<br />
Вы можете перенаправлять вывод команды другой команде, используя экземпляр TProcess для '''каждой''' команды.<br />
<br />
Вот пример, который объясняет, как перенаправить вывод одного процесса другому. Чтобы перенаправить вывод процесса в файл/поток, см. пример [[Executing_External_Programs/ru#.D0.A7.D1.82.D0.B5.D0.BD.D0.B8.D0.B5_.D0.B1.D0.BE.D0.BB.D1.8C.D1.88.D0.B8.D1.85_.D0.BE.D0.B1.D1.8A.D0.B5.D0.BC.D0.BE.D0.B2_.D0.B2.D1.8B.D0.B2.D0.BE.D0.B4.D0.B0|чтение больших объемов вывода]].<br />
<br />
Вы можете не только перенаправлять «обычный» вывод (также известный как stdout), но также можете перенаправить вывод ошибок (stderr), если вы укажете опцию poStderrToOutPut, как видно в настройках для второго процесса.<br />
<br />
<syntaxhighlight lang=pascal>program Project1;<br />
<br />
uses<br />
Classes, sysutils, process;<br />
<br />
var<br />
FirstProcess,<br />
SecondProcess: TProcess;<br />
Buffer: array[0..127] of char;<br />
ReadCount: Integer;<br />
ReadSize: Integer;<br />
begin<br />
FirstProcess := TProcess.Create(nil);<br />
SecondProcess := TProcess.Create(nil);<br />
<br />
FirstProcess.Options := [poUsePipes]; <br />
FirstProcess.Executable := 'pwd'; <br />
<br />
SecondProcess.Options := [poUsePipes,poStderrToOutPut];<br />
SecondProcess.Executable := 'grep'; <br />
SecondProcess.Parameters.Add(DirectorySeparator+ ' -'); <br />
// это было бы аналогично "pwd | grep / -"<br />
<br />
FirstProcess.Execute;<br />
SecondProcess.Execute;<br />
<br />
while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do<br />
begin<br />
if FirstProcess.Output.NumBytesAvailable > 0 then<br />
begin<br />
// убеждаемся, что мы не читаем больше данных, чем мы выделили<br />
// для этого места в буфере<br />
ReadSize := FirstProcess.Output.NumBytesAvailable;<br />
if ReadSize > SizeOf(Buffer) then<br />
ReadSize := SizeOf(Buffer);<br />
// теперь считываем вывод в буфер<br />
ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);<br />
// и пишем буфер во второй процесс<br />
SecondProcess.Input.Write(Buffer[0], ReadCount);<br />
<br />
// если SecondProcess записывает много данных в свой Output, то <br />
// мы должны прочитать эти данные здесь, чтобы предотвратить блокировку<br />
// см. предыдущий пример «Чтение больших объемов вывода»<br />
end;<br />
end;<br />
// Закрываем вход на SecondProcess,<br />
// поэтому он заканчивает обработку своих данных<br />
SecondProcess.CloseInput;<br />
<br />
// и дожидаемся его завершения <br />
// будьте осторожны с тем, какую команду вы запускаете, потому что она может не завершиться, <br />
// когда ее вход закроется, следующая строка может зациклиться навечно<br />
while SecondProcess.Running do<br />
Sleep(1);<br />
// вот и все! остальная часть программы просто в качестве примера<br />
// это немного «полезно»<br />
<br />
// мы будем повторно использовать буфер для вывода SecondProcess'а<br />
// вывод в *эту* программу stdout<br />
WriteLn('Grep output Start:');<br />
ReadSize := SecondProcess.Output.NumBytesAvailable;<br />
if ReadSize > SizeOf(Buffer) then<br />
ReadSize := SizeOf(Buffer);<br />
if ReadSize > 0 then<br />
begin<br />
ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);<br />
WriteLn(Copy(Buffer,0, ReadCount));<br />
end<br />
else<br />
WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);<br />
WriteLn('Grep output Finish:');<br />
<br />
// освобождаем объекты нашего процесса<br />
FirstProcess.Free;<br />
SecondProcess.Free;<br />
end.</syntaxhighlight><br />
<br />
Вот и все. Теперь вы можете перенаправлять вывод из одной программы в другую.<br />
<br />
==== Заметки ====<br />
Этот пример может показаться излишним, поскольку можно запускать «сложные» команды, используя оболочку с TProcess, например:<br />
<syntaxhighlight lang=pascal>Process.Commandline := 'sh -c "pwd | grep / -"';</syntaxhighlight><br />
<br />
Но наш пример более кроссплатформенный, так как он не нуждается в модификации для работы в Windows или Linux и т.д. "sh" может существовать или не существовать на вашей платформе и обычно доступен только на платформах *nix. Кроме того, у нас больше гибкости в нашем примере, так как вы можете читать и записывать из/в ввода, вывода и stderr каждого процесса в отдельности, что может быть очень выгодно для вашего проекта.<br />
<br />
===Перенаправление ввода и вывода и запуск под Root===<br />
Распространенная проблема в Unix(macOS) и Linux заключается в том, что вы хотите выполнить какую-либо программу под учетной записью root (или, в более общем случае, под другой учетной записью пользователя). В качестве примера можно привести команду ''ping''.<br />
<br />
Если вы можете использовать sudo для этого, вы можете приспособить следующий пример, адаптированный из примера, опубликованного andyman на форуме ([http://lazarus.freepascal.org/index.php/topic,14479.0.html]). Этот пример запускает <code>ls</code> в каталоге <code>/root</code>, но, конечно, может быть адаптирован.<br />
<br />
'''Лучший способ''' сделать это - использовать пакет policykit, который должен быть доступен во всех последних версиях Linux. Подробности смотрите в [http://lazarus.freepascal.org/index.php/topic,14479.0.html ветке форума.]<br />
<br />
Большие части этого кода похожи на предыдущий пример, но он также показывает, как перенаправить stdout и stderr процесса, вызываемого отдельно, в stdout и stderr нашего собственного кода.<br />
<br />
<syntaxhighlight lang=pascal><br />
program rootls;<br />
<br />
{Демонстрирует использование TProcess, перенаправляющего stdout/stderr в наш stdout/stderr,<br />
вызов sudo в Linux/OS и обеспечивающего ввод в stdin}<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
Classes,<br />
Math, {for min}<br />
Process;<br />
<br />
procedure RunsLsRoot;<br />
var<br />
Proc: TProcess;<br />
CharBuffer: array [0..511] of char;<br />
ReadCount: integer;<br />
ExitCode: integer;<br />
SudoPassword: string;<br />
begin<br />
WriteLn('Пожалуйста, введите пароль sudo:');<br />
Readln(SudoPassword);<br />
ExitCode := -1; //Начнем с неудачного вывода, посмотрим позже, работает ли это<br />
Proc := TProcess.Create(nil); //Создаем новый процесс<br />
try<br />
Proc.Options := [poUsePipes, poStderrToOutPut]; //Используем pipes для перенаправления программных stdin, stdout, stderr<br />
Proc.CommandLine := 'sudo -S ls /root'; //Запускаем ls /root как root с помощью sudo<br />
// -S заставляет sudo читать пароль из stdin<br />
Proc.Execute; //Запускаем его. sudo теперь, вероятно, попросит пароль<br />
<br />
// пишем пароль в stdin sudo программы:<br />
SudoPassword := SudoPassword + LineEnding;<br />
Proc.Input.Write(SudoPassword[1], Length(SudoPassword));<br />
SudoPassword := '%*'; // короткая строка, надеюсь, это немного зашифрует память; примечание: использование PChars более надежно<br />
SudoPassword := ''; // и сделает программу немного более безопасной от слежки?!?<br />
<br />
// основной цикл для чтения вывода из stdout и stderr sudo<br />
while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or<br />
(Proc.Stderr.NumBytesAvailable > 0) do<br />
begin<br />
// читаем stdout и пишем в наш stdout<br />
while Proc.Output.NumBytesAvailable > 0 do<br />
begin<br />
ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Читаем до буфера, не более<br />
Proc.Output.Read(CharBuffer, ReadCount);<br />
Write(StdOut, Copy(CharBuffer, 0, ReadCount));<br />
end;<br />
// читаем stderr и пишем в наш stderr<br />
while Proc.Stderr.NumBytesAvailable > 0 do<br />
begin<br />
ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Читаем до буфера, не более<br />
Proc.Stderr.Read(CharBuffer, ReadCount);<br />
Write(StdErr, Copy(CharBuffer, 0, ReadCount));<br />
end;<br />
end;<br />
ExitCode := Proc.ExitStatus;<br />
finally<br />
Proc.Free;<br />
Halt(ExitCode);<br />
end;<br />
end;<br />
<br />
begin<br />
RunsLsRoot;<br />
end.<br />
</syntaxhighlight><br />
<br />
Другие мысли:<br />
Без сомнения, было бы целесообразно проверить, запрашивает ли sudo пароль. Это можно последовательно проверить, установив переменную окружения SUDO_PROMPT в ту, которую мы наблюдаем при чтении стандартного вывода TProcess, чтобы избежать проблемы, связанной с тем, что приглашение будет различным для разных локалей. Установка переменной среды приводит к тому, что значения по умолчанию очищаются (наследуются от нашего процесса), поэтому мы должны при необходимости скопировать окружение из нашей программы.<br />
<br />
=== Использование fdisk с sudo в Linux ===<br />
В следующем примере показано, как запустить fdisk на компьютере с Linux с помощью команды sudo для получения прав root. <br />
{{Note| это только пример, и он не рассчитан на большой результат}}<br />
<br />
<syntaxhighlight lang=pascal><br />
program getpartitioninfo;<br />
<br />
{Первоначально пример предоставлен пользователем wjackson153 с форума Lazarus. Пожалуйста, свяжитесь с ним в случае возникновения вопросов, замечаний и т.д. Пример преобразован для простоты понимания/краткости из фрагмента кода Lazarus в программу FPC пользователем BigChimp}<br />
<br />
Uses<br />
Classes, SysUtils, FileUtil, Process;<br />
<br />
var<br />
hprocess: TProcess;<br />
sPass: String;<br />
OutputLines: TStringList;<br />
<br />
begin <br />
sPass := 'yoursudopasswordhere'; // Вы должны изменить это на свой собственный пароль sudo<br />
OutputLines:=TStringList.Create; //... было бы неплохо убедиться с помощью блока try..finally, <br />
// что OutputLines освобожден ... То же самое для hProcess.<br />
<br />
// Следующий пример откроет fdisk в фоновом режиме и даст нам информацию о разделе<br />
// Поскольку fdisk требует повышенных привилегий, нам нужно <br />
// передать наш пароль в качестве параметра sudo с помощью параметра -S,<br />
// поэтому он будет ждать, пока наша программа отправит наш пароль в sudo приложение<br />
hProcess := TProcess.Create(nil);<br />
// В Linux/Unix/macOS нам нужно указать полный путь к нашему исполняемому файлу:<br />
hProcess.Executable := '/bin/sh';<br />
// Теперь мы добавляем все параметры в командную строку:<br />
hprocess.Parameters.Add('-c');<br />
// Здесь мы передаем пароль в команду sudo, которая затем выполняет fdisk -l: <br />
hprocess.Parameters.add('echo ' + sPass + ' | sudo -S fdisk -l');<br />
// Запускаем асинхронно (дожидаемся завершения процесса) и используем pipes, чтобы мы могли прочитать output pipe<br />
hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];<br />
// Теперь запускаем:<br />
hProcess.Execute;<br />
<br />
// hProcess должен запустить внешний исполняемый файл (потому что мы используем poWaitOnExit).<br />
// Теперь вы можете обработать output процесса (стандартный output и стандартный error), например:<br />
OutputLines.Add('stdout:');<br />
OutputLines.LoadFromStream(hprocess.Output);<br />
OutputLines.Add('stderr:');<br />
OutputLines.LoadFromStream(hProcess.Stderr);<br />
// Показываем output на экране:<br />
writeln(OutputLines.Text);<br />
<br />
// Очищаем, чтобы избежать утечек памяти:<br />
hProcess.Free;<br />
OutputLines.Free;<br />
<br />
//Ниже приведены некоторые примеры, как вы видите, мы можем передавать недопустимые символы, как если бы это было сделано из терминала<br />
//Даже, если вы где-то читали, что это невозможно, я уверяю вас, что при помощи этого метода вы сможете :)<br />
<br />
//hprocess.Parameters.Add('ping -c 1 www.google.com');<br />
//hprocess.Parameters.Add('ifconfig wlan0 | grep ' + QuotedStr('inet addr:') + ' | cut -d: -f2');<br />
<br />
//Использование QuotedStr() не является обязательным требованием, хотя делает его более читабельным кодом;<br />
//Вы можете использовать двойные кавычки и добиться того же эффекта<br />
<br />
//hprocess.Parameters.Add('glxinfo | grep direct'); <br />
<br />
// Этот метод также можно использовать для установки приложений из вашего хранилища:<br />
<br />
//hprocess.Parameters.add('echo ' + sPass + ' | sudo -S apt-get install -y pkg-name'); <br />
<br />
end. <br />
</syntaxhighlight><br />
<br />
===Параметры, содержащие пробелы (замена кавычек оболочки Shell)===<br />
<br />
В оболочке Linux можно записать в кавычки следующие аргументы:<br />
<br />
<syntaxhighlight lang=bash>gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram</syntaxhighlight><br />
<br />
И GDB получит 3 аргумента (в дополнение к первому аргументу, который является полным путем к исполняемому файлу):<br />
#--batch<br />
#--eval-command=info symbol 0x0000DDDD<br />
#полный путь к myprogram<br />
<br />
TProcess также может передавать параметры, содержащие пробелы, но использует другой стиль квотирования. Вместо того, чтобы заключать в кавычки только часть параметра, заключите в кавычки все из них. Вот так:<br />
<br />
<syntaxhighlight lang=bash>TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';</syntaxhighlight><br />
<br />
И также не забудьте передать только полный путь.<br />
<br />
Смотрите также эту дискуссию об этом: [http://bugs.freepascal.org/view.php?id=14446 здесь]<br />
<br />
==Альтернативные решения с помощью LCLIntf==<br />
Иногда вам не нужно явно вызывать внешнюю программу, чтобы получить необходимую вам функциональность. Вместо того, чтобы открывать приложение и указывать документ для него, просто попросите ОС открыть документ, позволив ей использовать приложение по умолчанию, связанное с этим типом файла. Ниже приведены некоторые примеры.<br />
<br />
===Открытие документа в приложении по умолчанию===<br />
<br />
В некоторых ситуациях вам нужно открыть какой-либо документ/файл с использованием приложения по умолчанию, а не выполнить определенную программу. Это зависит от запущенной операционной системы. Lazarus предоставляет платформ-независимую процедуру '''OpenDocument''', которая будет делать это за вас. Ваше приложение продолжит работу, не дожидаясь закрытия процесса документа.<br />
<br />
<syntaxhighlight lang=pascal><br />
uses LCLIntf;<br />
...<br />
OpenDocument('manual.pdf'); <br />
...<br />
</syntaxhighlight><br />
<br />
* [[opendocument|справка по OpenDocument]]<br />
<br />
===Открытие web-страницы в web-браузере по умолчанию===<br />
<br />
Просто передайте требуемый URL, ''http://'' в начале строки является необязательным при определенных условиях. Кроме того, передача имени файла дает те же результаты, что и OpenDocument()<br />
<br />
<syntaxhighlight lang=pascal><br />
uses LCLIntf;<br />
...<br />
OpenURL('www.lazarus.freepascal.org/');<br />
</syntaxhighlight><br />
<br />
См. также:<br />
* [[OpenURL|справка по OpenURL]]<br />
<br />
Кроме того, вы можете использовать '''TProcess''' как в данном примере:<br />
<syntaxhighlight lang=pascal><br />
uses Process;<br />
<br />
procedure OpenWebPage(URL: string);<br />
// Очевидно, вам нужно передать свой URL внутри кавычек "", например, "www.lazarus.freepascal.org"<br />
var<br />
Browser, Params: string;<br />
begin<br />
FindDefaultBrowser(Browser, Params);<br />
with TProcess.Create(nil) do<br />
try<br />
Executable := Browser;<br />
Params:=Format(Params, [URL]);<br />
Params:=copy(Params,2,length(Params)-2); // удаляем "", новая версия TProcess.Parameters делает это сама<br />
Parameters.Add(Params);<br />
Options := [poNoConsole];<br />
Execute;<br />
finally<br />
Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
==См. также==<br />
* [http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html Документация по TProcess]<br />
* [[opendocument|OpenDocument]]<br />
* [[OpenURL]]<br />
* [[TProcessUTF8/ru|TProcessUTF8]]<br />
* [[TXMLPropStorage/ru|TXMLPropStorage]]<br />
* [[Webbrowser/ru|Webbrowser]]<br />
* [http://freepascal.ru/article/freepascal/20191121080000/ Запуск чужой программы из своей] - статья на русскоязычном freepascal-ресурсе</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=156610Autosize / Layout/ru2023-06-14T14:25:49Z<p>Zoltanleo: /* Автомасштаб */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<code>AutoSize</code>(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно <code>AutoSize</code> равенTrue и делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота <code>TButton</code> изменяется в соответствии с надписью, в то время как <code>TEdit</code> изменяется только в высоту. Ширина <code>TEdit</code> не изменяется автоматически. Вы можете изменить ширину <code>TEdit</code> самостоятельно.(см GetPreferredSize).<br />
* Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства <code>BorderSpacing</code>) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* <code>AutoSize</code> в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из <code>TLabel</code> изменяется. В LCL <code>AutoSize</code> всегда активен. Delphi позволяет изменить размер <code>TLabel</code>, который имеет AutoSize = True, LCL - нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании <code>akRight</code>, <code>akBottom</code> якоря с установленными <code>AnchorSides</code> и <code>BorderSpacing</code> сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью свойства кнопки <code>AutoSize=false</code> задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке <code>AutoSize=true</code> для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
<code>AutoSize</code> не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод <code>GetPreferredSize</code> элемента управления, который вызывает метод <code>CalculatePreferredSize</code>. По умолчанию реализация <code>TWinControl</code> запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод <code>CalculatePreferredSize</code>. Например, <code>TImage</code> переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг <code>ControlStyle = csAutoSize</code>, который в настоящее время используется только <code>TPanel</code>).<br />
<br />
<code>TWinControl</code> вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со значением привязки <code>Align=alTop</code> элемент управления привязан слева и справа, и соответствует ширине Родителя. Если <code>Parent.AutoSize</code> имеет значение true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод <code>GetControlClassDefaultSize</code>. То же самое для свойства <code>Height</code> и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 22:53, 26 September 2018 (CEST): на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный [в упомянутых выше свойствах] зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" [в родительский контрол] [дочерним] элементам управления.<br />
<br />
[Родительские] элементы управления могут отключать перемещение дочерних элементов управления установкой [у свойства] ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=156609Autosize / Layout/ru2023-06-14T14:04:02Z<p>Zoltanleo: /* Фиксированный дизайн */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен в значение <code>[akLeft, akTop]</code>, что означает, значения Top и Left не будут изменяться LCL. Если значение <code>AutoSize</code> равно False, LCL не изменяет ширину или высоту. Если значение <code>AutoSize</code> равно True, то ширина и высота изменяются по размеру содержимого. Например, <code>TLabel.AutoSize</code> умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и <code>TLabel</code>, чтобы измененный текст поместился в контроле полностью. <code>TButton.AutoSize</code> умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения <code>Button.AutoSize</code> в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении <code>Caption</code> на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства <code>Left</code>, <code>Top</code>, <code>Width</code> и <code>Height</code>.<br />
<br />
=Автомасштаб=<br />
<br />
<var>AutoSize</var> логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно AutoSize = True делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота TButton изменяется в соответствии с надписью, в то время как TEdit изменяется только в высоту. Ширина TEdit не изменяется автоматически. Вы можете изменить ширину TEdit. (см GetPreferredSize).<br />
* Она передвигает все фиксированно расположенные дочерние элементы управления, так чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от BorderSpacing) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* AutoSize в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из TLabel изменяется.LCL AutoSize всегда активен. Delphi позволяет изменить размер TLabel, который имеет AutoSize = True, LCL нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании akRight, akBottom якоря с установленными AnchorSides и BorderSpacing сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью [свойства] кнопки AutoSize=false задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке AutoSize=true для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
AutoSize не сжимает [элемент управления] до минимально возможного размера, как вы можете видеть в кнопке OK. Он использует метод GetPreferredSize элемента управления, который вызывает метод CalculatePreferredSize. По умолчанию реализация TWinControl запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) [элемента управления] (если не установлен флаг ControlStyle csAutoSize0x0, который в настоящее время используется только TPanel'ью).<br />
<br />
TWinControl вычисляет размер всех [своих] дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со [значением привязки] ''Align=alTop'' элемент управления привязан слева и справа, и соответствует ширине Родителя. Если ''Parent.AutoSize'' [имеет значение] true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если [значение] оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод '''GetControlClassDefaultSize'''. То же самое для [свойства] '''Height''' и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 22:53, 26 September 2018 (CEST): на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный [в упомянутых выше свойствах] зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" [в родительский контрол] [дочерним] элементам управления.<br />
<br />
[Родительские] элементы управления могут отключать перемещение дочерних элементов управления установкой [у свойства] ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=156608Autosize / Layout/ru2023-06-14T13:54:44Z<p>Zoltanleo: /* Общие свойства */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен на [akLeft, akTop], что означает, значения Top и Left не будут изменяться LCL. Если AutoSize равно False, LCL не изменяет ширину или высоту. Если AutoSize равно True, то ширина и высота изменяются по размеру содержимого. Например, TLabel.AutoSize умолчанию True и тем самым изменяя его текст будет изменяться размер TLabel, чтобы поместился измененный текст. TButton.AutoSize умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке Button.AutoSize в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении Caption на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства Left, Top, Width и Height.<br />
<br />
=Автомасштаб=<br />
<br />
<var>AutoSize</var> логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно AutoSize = True делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота TButton изменяется в соответствии с надписью, в то время как TEdit изменяется только в высоту. Ширина TEdit не изменяется автоматически. Вы можете изменить ширину TEdit. (см GetPreferredSize).<br />
* Она передвигает все фиксированно расположенные дочерние элементы управления, так чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от BorderSpacing) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* AutoSize в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из TLabel изменяется.LCL AutoSize всегда активен. Delphi позволяет изменить размер TLabel, который имеет AutoSize = True, LCL нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании akRight, akBottom якоря с установленными AnchorSides и BorderSpacing сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью [свойства] кнопки AutoSize=false задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке AutoSize=true для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
AutoSize не сжимает [элемент управления] до минимально возможного размера, как вы можете видеть в кнопке OK. Он использует метод GetPreferredSize элемента управления, который вызывает метод CalculatePreferredSize. По умолчанию реализация TWinControl запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) [элемента управления] (если не установлен флаг ControlStyle csAutoSize0x0, который в настоящее время используется только TPanel'ью).<br />
<br />
TWinControl вычисляет размер всех [своих] дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со [значением привязки] ''Align=alTop'' элемент управления привязан слева и справа, и соответствует ширине Родителя. Если ''Parent.AutoSize'' [имеет значение] true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если [значение] оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод '''GetControlClassDefaultSize'''. То же самое для [свойства] '''Height''' и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 22:53, 26 September 2018 (CEST): на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный [в упомянутых выше свойствах] зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" [в родительский контрол] [дочерним] элементам управления.<br />
<br />
[Родительские] элементы управления могут отключать перемещение дочерних элементов управления установкой [у свойства] ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=156607Autosize / Layout/ru2023-06-14T13:53:16Z<p>Zoltanleo: /* Правила приоритета */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout <br />
# AutoSize (Автомасштаб элемента)<br />
# События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Несколько важных свойств могут быть изменены, чтобы настроить автоматическое изменение размера:<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен на [akLeft, akTop], что означает, значения Top и Left не будут изменяться LCL. Если AutoSize равно False, LCL не изменяет ширину или высоту. Если AutoSize равно True, то ширина и высота изменяются по размеру содержимого. Например, TLabel.AutoSize умолчанию True и тем самым изменяя его текст будет изменяться размер TLabel, чтобы поместился измененный текст. TButton.AutoSize умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке Button.AutoSize в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении Caption на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства Left, Top, Width и Height.<br />
<br />
=Автомасштаб=<br />
<br />
<var>AutoSize</var> логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно AutoSize = True делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота TButton изменяется в соответствии с надписью, в то время как TEdit изменяется только в высоту. Ширина TEdit не изменяется автоматически. Вы можете изменить ширину TEdit. (см GetPreferredSize).<br />
* Она передвигает все фиксированно расположенные дочерние элементы управления, так чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от BorderSpacing) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* AutoSize в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из TLabel изменяется.LCL AutoSize всегда активен. Delphi позволяет изменить размер TLabel, который имеет AutoSize = True, LCL нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании akRight, akBottom якоря с установленными AnchorSides и BorderSpacing сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью [свойства] кнопки AutoSize=false задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке AutoSize=true для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
AutoSize не сжимает [элемент управления] до минимально возможного размера, как вы можете видеть в кнопке OK. Он использует метод GetPreferredSize элемента управления, который вызывает метод CalculatePreferredSize. По умолчанию реализация TWinControl запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) [элемента управления] (если не установлен флаг ControlStyle csAutoSize0x0, который в настоящее время используется только TPanel'ью).<br />
<br />
TWinControl вычисляет размер всех [своих] дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со [значением привязки] ''Align=alTop'' элемент управления привязан слева и справа, и соответствует ширине Родителя. Если ''Parent.AutoSize'' [имеет значение] true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если [значение] оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод '''GetControlClassDefaultSize'''. То же самое для [свойства] '''Height''' и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 22:53, 26 September 2018 (CEST): на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный [в упомянутых выше свойствах] зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" [в родительский контрол] [дочерним] элементам управления.<br />
<br />
[Родительские] элементы управления могут отключать перемещение дочерних элементов управления установкой [у свойства] ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout/ru&diff=156606Autosize / Layout/ru2023-06-14T13:39:52Z<p>Zoltanleo: /* Фиксированный дизайн */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
<br />
=Введение=<br />
<br />
LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д. <br />
<br />
*[[#Фиксированный дизайн|Фиксированный дизайн (Fixed design)]]: это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно. <br />
*[[Autosize_/_Layout/ru#Выравнивание (Aligned)|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.<br />
*[[Autosize_/_Layout/ru#Разметка (Layout)|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)<br />
*[[Autosize_/_Layout/ru#Пользовательская разметка через OnResize/OnChangeBounds|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.<br />
*[[Autosize_/_Layout/ru#Пользовательские элементы управления|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.<br />
<br />
==Правила приоритета==<br />
<br />
# Constraints (Ограничения)<br />
# Align (Выравнивание) <br />
# Anchors(Привязки)<br />
# ChildSizing.Layout<br />
# AutoSize (Автомасштаб элемента)<br />
# OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл<br />
<br />
==Общие свойства==<br />
<br />
Несколько важных свойств могут быть изменены, чтобы настроить автоматическое изменение размера:<br />
<br />
*Left, Top, Width, Height<br />
*[[#Автомасштаб|AutoSize]]: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления<br />
*[[Autosize_/_Layout/ru#Привязка сторон (Anchored)|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне #Label.<br />
*[[#Align|Align]]<br />
*Constraints: позволяет установить минимум и максимум для ширины и высоты<br />
*[[Autosize_/_Layout/ru#Зазор|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления<br />
*[[Autosize_/_Layout/ru#Планировка (Layout)|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления<br />
<br />
Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].<br />
<br />
=Фиксированный дизайн=<br />
<br />
Фиксированный дизайн установлен по умолчанию. Якорь установлен на [akLeft, akTop], что означает, значения Top и Left не будут изменяться LCL. Если AutoSize равно False, LCL не изменяет ширину или высоту. Если AutoSize равно True, то ширина и высота изменяются по размеру содержимого. Например, TLabel.AutoSize умолчанию True и тем самым изменяя его текст будет изменяться размер TLabel, чтобы поместился измененный текст. TButton.AutoSize умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке Button.AutoSize в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении Caption на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается.<br />
В любой момент вы можете изменить свойства Left, Top, Width и Height.<br />
<br />
=Автомасштаб=<br />
<br />
<var>AutoSize</var> логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.<br />
<br />
Обычно AutoSize = True делает две вещи для видимого элемента управления:<br />
* Если возможно, изменяет управление в нужный размер. Например ширина и высота TButton изменяется в соответствии с надписью, в то время как TEdit изменяется только в высоту. Ширина TEdit не изменяется автоматически. Вы можете изменить ширину TEdit. (см GetPreferredSize).<br />
* Она передвигает все фиксированно расположенные дочерние элементы управления, так чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от BorderSpacing) и самый верхний дочерний элемент управления имел Top = 0.<br />
<br />
==Отличия от Delphi==<br />
<br />
* AutoSize в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из TLabel изменяется.LCL AutoSize всегда активен. Delphi позволяет изменить размер TLabel, который имеет AutoSize = True, LCL нет.<br />
* Скрытые управления не изменяют размер автоматически.<br />
* Изменение размера элемента управления не изменяет размер/расположение заякоренных дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании akRight, akBottom якоря с установленными AnchorSides и BorderSpacing сохраняют правильное расстояние.<br />
<br />
==Автомасштаб и изменение размера элемента управления==<br />
<br />
С помощью [свойства] кнопки AutoSize=false задаются фиксированный размер по умолчанию.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
При установке AutoSize=true для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
AutoSize не сжимает [элемент управления] до минимально возможного размера, как вы можете видеть в кнопке OK. Он использует метод GetPreferredSize элемента управления, который вызывает метод CalculatePreferredSize. По умолчанию реализация TWinControl запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) [элемента управления] (если не установлен флаг ControlStyle csAutoSize0x0, который в настоящее время используется только TPanel'ью).<br />
<br />
TWinControl вычисляет размер всех [своих] дочерних элементов управления и использует это для вычисления своего оптимального размера.<br />
<br />
Когда элемент управления привязан как слева, так и справа, его ''ширина фиксирована''. Например, со [значением привязки] ''Align=alTop'' элемент управления привязан слева и справа, и соответствует ширине Родителя. Если ''Parent.AutoSize'' [имеет значение] true, тогда ''Родитель'' будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize_/_Layout/ru#Выравнивание и свойство AutoSize|Выравнивание и свойство AutoSize]]. Если [значение] оптимальной ширины недоступно, используются последние установленные границы ('''BaseBounds''' или '''ReadBounds'''). Если никаких ограничений не было установлено, используется метод '''GetControlClassDefaultSize'''. То же самое для [свойства] '''Height''' и привязки сверху и снизу.<br />
<br />
Ограничения применяются всегда и имеют приоритет.<br />
<br />
==Автомасштаб и перемещение дочерних элементов управления==<br />
<br />
Когда ''AutoSize=false'', вы можете размещать и перемещать элементы управления свободно:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
Когда ''AutoSize=true'', дочерние элементы управления с фиксированным положением перемещаются с подгонкой.<br />
<br />
<br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 22:53, 26 September 2018 (CEST): на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.<br />
----<br />
<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства ''BorderSpacing>0'' (у кнопок) или ''Panel.ChildSizing.LeftRightSpacing>0'' (у панели), то кнопки будут перемещены так, чтобы предопределенный [в упомянутых выше свойствах] зазор был задействован.<br />
<br />
Могут быть перемещены дочерние элементы управления только со следующими свойствами:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
Перемещение дочерних элементов управления зависит от свойства [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. Разметка применяется в методе ''TWinControl.AlignControls'', который может быть полностью или частично переопределен. Например, ''TToolBar'' переопределяет ''ControlsAligned'', чтобы разместить все элементы управления со значением свойства ''Align=alCustom'', и задает разметку для перемещения на следующие линии не "вписавшимся" [в родительский контрол] [дочерним] элементам управления.<br />
<br />
[Родительские] элементы управления могут отключать перемещение дочерних элементов управления установкой [у свойства] ''ControlStyle'' флагов ''csAutoSizeKeepChildLeft'' и ''csAutoSizeKeepChildTop'' (начиная с 0.9.29).<br />
<br />
==Автомасштаб и формы==<br />
<br />
Формы без Parent [родительского элемента управления] управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера [в RunTime] является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 [px], а widgetset в ответ изменит размер к 800 [px]. Если вы устанавливаете [свойство] ''Witdh'' в [событии формы] ''OnResize'', то можете создать бесконечную петлю. Вот почему LCL TForm отключает ''AutoSize'', когда widgetset изменяет размеры формы.<br />
<br />
Это означает, что [действие свойства] ''AutoSize'' для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся [текущие] границы [окна]. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как магнитные [("прилипающие")] края, которые изменяют размеры [текущего] окна в привязке к другим окнам.<br />
<br />
===Принудительная установка автомасштабирования формы===<br />
Вы можете выставить новое [значение свойства] ''AutoSize'', выполнив:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Расчет размера формы с "автомасштабом" заранее===<br />
<br />
При размещении формы с "автомасштабом" [(очевидно, имеется ввиду ''Form1.AutoSize=True'')] вам может потребоваться [получить] размер формы, прежде чем показывать ее. Для [установки] автомасштаба требуется дескриптор [хэндл окна]. Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
{{Note| Диспетчер окон и события формы могут изменять размер [окна]. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.}}<br />
<br /><br /><br />
<br />
=Привязка сторон (Anchored)=<br />
<br />
Элементы управления [для привязки к] четырем сторонам имеют [свойство ''Anchors'' с возможными значениями]: ''akLeft'', ''akTop'', ''akRight'' и ''akBottom''. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает '''сохранение расстояния''' [между привязанными элементами управления]. По умолчанию [значение свойства] ''Anchors = [akLeft, akTop]''. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как ''Align'' и ''Parent.AutoSize'' имеют более высокий приоритет, и могут изменять поведение.<br />
<br />
==Привязка к родителю или nil==<br />
<br />
Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.<br />
<br />
Существует четыре комбинации akLeft, akRight (akTop, akBottom):<br />
<br />
*'''akLeft, no akRight''': '''Left''' контрола фиксирован и не [может быть] изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что '''Width''' также сохраняется.<br />
*'''akLeft and akRight''': '''Left''' элемента управления фиксирован и не [может быть] изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда '''Width''' контрола также будет увеличена на 100 пикселей.<br />
*'''akRight, no akLeft''': левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.<br />
*'''no akLeft and no akRight''': ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.<br />
<br />
===Изменение размера родителя с привязанным контролом===<br />
<br />
При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено [свойство] AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.<br />
<br />
GroupBox с кнопкой: [[Image:Anchors1.png]]<br />
<br />
Когда GroupBox увеличивается [в размере], расстояние до привязанной стороны сохраняется:<br />
<br />
С akLeft, без akRight: [[Image:Anchors akLeft.png]]<br />
<br />
С akLeft и akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
С akRight, без akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Три кнопки в GroupBox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
Без akLeft и без akRight их центры масштабируются: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''На заметку:'''<br />
*Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений [, сохраненных в файле формы] lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.<br />
*Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.<br />
<br />
===Изменение размера привязанного элемента управления===<br />
<br />
При изменении ширины привязанного элемента управления, например, через Object Inspector или через код ''Button1.Width:=3'', вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:<br />
====Привязка к nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя [значение] ''Left'' кнопки, увеличивая расстояние от правой стороны [кнопки до правого края].<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] При изменении размера Groupbox кнопка сохранит новое расстояние [до правого края].<br />
<br />
'''Объяснение:''' установка [значения] '''Width''' [кнопки] эквивалентна вызову SetBounds('''Left''',Top,'''NewWidth''',Height). Вот почему [значение] '''Left''' сохраняется. Это совместимо с [поведением контролов в] Delphi.<br />
<br />
====Привязка к родителю====<br />
[[Image:Anchored_right_parent_resize1.png]] Кнопка Button1 привязана [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Установка [значения] ''Width'' на меньшее значение приведет к сужению кнопки, сохранив расстояние [от ее правой границы до края компонента-родителя] и изменив [значение] ''Left'' кнопки.<br />
<br />
==Привязка к соседу==<br />
<br />
Вы можете привязываться к соседним элементам управления. Следующий пример показывает[, что]:<br />
*вы можете привязать левый [край] label'а к левому краю кнопки<br />
*привязать верхний край label'а к нижнему краю кнопки<br />
*привязать центр label'а к центру кнопки<br /><br />
[[Image:Anchorsides example1.png]]<br /><br />
<br />
[[Image:Anchorside example2.png]]<br /><br /><br />
<br />
Подробнее о том, как установить привязку, см. [[Anchor_Sides/ru|Стороны привязки]].<br /><br /><br />
[[Image:Anchoreditor.png]]<br />
<br />
==Зазор==<br />
<br />
Свойство BorderSpacing[(зазор)] управляет минимальным количеством пространства вокруг элемента управления. Свойства:<br />
<br />
*'''Around''': эта величина в пикселах, [которая] добавляется к [каждому значению] Left, Top, Right, Bottom [элемента управления].<br />
*'''Left''': зазор в пикселах с левой стороны элемента управления<br />
*'''Top''': зазор в пикселах сверху элемента управления<br />
*'''Right''': зазор в пикселах с правой стороны элемента управления<br />
*'''Bottom''': зазор в пикселах под элементом управления<br />
*'''InnerBorder''': это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.<br />
*'''CellAlignHorizontal''': Этот [параметр] используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.<br />
*'''CellAlignVertical''': так же, как CellAlignHorizontal, но для вертикального выравнивания.<br />
<br />
===Правила зазоров===<br />
<br />
*[Значение] Around добавляется к зазору для Left,Top,Right,Bottom [элемента управления].<br />
*Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют [им] растягиваться.<br />
<br />
====Привязка к противоположной стороне====<br />
<br />
Например, правая сторона A к левой стороне B.<br />
<br />
Используются обе границы A и B.<br />
<br />
*Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на [общем] родителе) он максимален[, если:]<br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на [общем] родителе) он максимален[, если:]<br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
Например: <br />
*если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px<br />
*если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px <br />
*если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px<br />
<br />
====Привязка к этой же стороне====<br />
<br />
Например, правая сторона A к правой стороне B.<br />
<br />
*Задействуется только зазор у границы А.<br />
*Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.<br />
<br />
====Привязка к центру====<br />
<br />
Например, центр А привязан к центру В.<br />
<br />
Не задействуются ни зазор, ни [свойство] Parent.ChildSizing.Horizontal/VerticalSpacing.<br />
<br />
====Пример====<br />
<br />
Общим примером является привязка Label'а к Edit'у.<br /><br />
<br />
[[Image:BorderSpacing Anchors.png]]<br /><br />
<br />
Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к [наличию] зазора в 6px между Label'ом и Edit'ом и [наличию] зазора в 6px слева от Label'а, а также [наличию] зазора в 6px справа от Edit'а. [Также еще] существует 6 пикселей зазора выше и ниже Edit'а.<br />
<br />
*Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, [когда]<br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, [когда]<br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все [остальные] зазоры игнорируются.<br />
<br />
*Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является<br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Вот более сложный пример:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
Важные части кода lfm:<br />
<br />
<syntaxhighlight lang="pascal"><br />
object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===Зазор и выравнивание===<br />
<br />
Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.<br />
*Обычно два Memo заполняют весь GroupBox.<br />
*Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет [присутствовать зазор в] 10 пикселей.<br />
*Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который [от Memo1 до] Memo2 [составит] 20px.<br />
*Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется [зазор в] 50 пикселей.<br />
*GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через [значения свойств] ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все [упомянутые значения равны] 0).<br /><br />
<br />
[[Image:Borderspacing align1.png]]<br /> <br /><br />
<br />
=Выравнивание (Aligned)=<br />
<br />
Свойство Align[(выравнивание)] работает почти так же, как в Delphi, и может использоваться для быстрого заполнения [какой-нибудь] области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient.<br />
Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия [друг друга]. Это означает, что все элементы управления [свойства] Align [со значением] alLeft, alTop, alBottom, alRight не будут перекрываться, если есть достаточно места.<br />
<br />
Алгоритм работает следующим образом:<br />
* Сначала все элементы управления с помощью [значения свойства] alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина [всех элементов остается] максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и [значение свойства] Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.<br />
*Затем все элементы управления с помощью [значения свойства] alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.<br />
*Потом все элементы управления с помощью [значения свойства] alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Затем все элементы управления с помощью [значения свойства] alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.<br />
*Если есть элемент управления с [установленным свойством] alClient, он заполнит оставшуюся [часть] клиентской [области].<br />
*Если имеется более одного элемента управления с [установленным свойством] alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них [будет] показан. <br /><br /><br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Выравнивание и зазоры==<br />
<br />
Величина [свойства] BorderSpacing и [свойства] ChildSize родителя применяется к выравниваемым элементам управления. [Элемент управления] memo ниже имеет Align=alClient.<br /><br />
<br />
----<br />
--[[User:Zoltanleo|Zoltanleo]] 15:27, 11 October 2018 (CEST): Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.<br />
----<br />
<br /><br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Выравнивание и Привязка==<br />
<br />
Свободная сторона выровненного элемента управления (напр., правая сторона '''Align=alLeft''') следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять [значение] своей '''Width'''. Если привязка установлена, то [значение] '''Width''' изменится.<br />
<br />
==Выравнивание и свойство AutoSize==<br />
<br />
Элемент управления, выровненный [посредством] alLeft или alRight, растягивается по вертикали и будет использовать свою ширину[, определенную на этапе проектирования формы]. Если [свойство] ''AutoSize'' установлено в ''true'', то [значение параметра] ''Width'' будет оптимальной шириной. Кнопка снизу имеет ''Align=alRight'' и ''AutoSize=true''.<br /><br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Выравнивание и свойство AutoSize родителя==<br />
<br />
Элемент управления, выровненный [посредством] alClient, заполняет оставшееся пространство. Родительский элемент управления со [значением свойства] ''AutoSize=true'' [при этом] будет растягиваться или сжиматься [так], чтобы впритирку умещать [на себе] свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?<br /><br />
<br />
[[Image:Autosize nested1.png]]<br /><br />
<br />
LCL использует ''предпочтительный размер'' кнопок и потому растягивает или сжимает groupbox:<br />
<br />
[[Image:Autosize nested2.png]]<br /><br />
<br />
==alCustom==<br />
<br />
Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления [с установленным значением] alCustom не перемещаются LCL, но [могут быть перемещены вашим] пользовательским элементом управления. [Для этого] вы должны переопределить [методы] CalculatePreferredSize и DoAutoSize.<br />
<br />
==Порядок расположения элементов управления с одинаковым [значением] Align==<br />
<br />
Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим [значением] Left, для alTop - наименьшим [значением] Top, для alRight - наибольшая [величина] Left+Width, для alBottom - наибольшая [величина] Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.<br />
<br />
{{Note| до [версии Lazarus] 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию [элементов управления] и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с [выравниванием] alTop или alLeft без указания границ, [все] элементы управления будут помещены в [координаты] 0.0, и, следовательно, последние добавленные [окажутся] в выигрыше [там], где раньше [выигрывал] первый.}}<br /><br /><br />
<br />
=Разметка (Layout)=<br />
<br />
==Строки, столбцы и строки==<br />
<br />
Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)<br /><br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
Свойство '''Layout''' по умолчанию [имеет значение] cclNone. Если вы установите '''Layout''' в другое значение, каждый дочерний элемент, имеющий ''нормальные'' привязки, будет выровнен. ''Нормальные'' привязки означают:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
Значение '''cclLeftToRightThenTopToBottom''' будет помещать первый дочерний элемент управление вверху [и] слева, второй - [вверху и] правее, и так далее. Это - '''линия'''. Свойство '''ControlsPerLine''' определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует [всего] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет [по] 3 элемента управления (столбцы).<br />
Если ''ControlsPerLine'' равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.<br />
<br />
Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не [приходит] из LCL.<br />
<br />
==Фиксированные зазоры между строками и столбцами==<br />
<br />
Вы можете добавить зазоры между [строками и столбцами] этими свойствами:<br />
<br />
*ChildSizing.VerticalSpacing - зазор между строками<br />
*ChildSizing.HorizontalSpacing - зазор между столбцами<br />
*ChildSizing.LeftRightSpacing - зазор между левой и правой [стороной] ''всех'' столбцов<br />
*ChildSizing.TopBottomSpacing - зазор выше и ниже ''всех'' столбцов<br />
<br />
Выше приведенный пример с [параметрами]:<br /><br />
ChildSizing.VerticalSpacing=6,<br /> <br />
ChildSizing.HorizontalSpacing=15,<br /> <br />
ChildSizing.LeftRightSpacing=30,<br /> <br />
ChildSizing.TopBottomSpacing=10,<br /> <br />
AutoSize=true<br /><br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с [помощью] его свойства BorderSpacing.<br />
<br />
==Растяжение==<br />
<br />
Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.<br />
<br />
*<tt>crsAnchorAligning</tt> - не использует дополнительные зазоры<br />
*<tt>crsScaleChilds</tt> - умножает ширину/высоту на тот же коэффициент<br />
*<tt>crsHomogeneousChildResize</tt> - добавляет к каждой ширине/высоте ту же величину<br />
*<tt>crsHomogeneousSpaceResize</tt> - добавляет к каждому зазору между дочерними элементами ту же величину<br />
<br />
===crsScaleChilds===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsScaleChilds</tt><br /> <tt>ChildSizing.EnlargeVertical=crsScaleChilds</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousChildResize</tt><br /> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
<tt>ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize</tt><br /> <tt>ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize</tt><br/> <tt>AutoSize=false</tt><br /><br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится [дополнительно по] 10 пикселей зазора слева, справа и между каждым дочерним элементом.<br />
<br />
==Сжатие==<br />
<br />
Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Отдельные ячейки==<br />
<br />
В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю ''ячейку''. ''Ячейка'' - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств '''BorderSpacing.CellAlignHorizontal''' и '''BorderSpacing.CellAlignVertical'''.<br />
<br />
Например, установите ''BorderSpacing.CellAlignHorizontal'' пятой кнопки в '''caCenter''' [и] вы получите следующее:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:<br />
<br />
*<tt>caFill</tt>: дочерний элемент управления заполняет всю ширину (высоту) ячейки<br />
*<tt>caCenter</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке<br />
*<tt>caLeftTop</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева<br />
*<tt>caRightBottom</tt>: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю <br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br /><br /><br />
<br />
=Пользовательская разметка через OnResize/OnChangeBounds=<br />
<br />
Иногда разметка LCL недостаточно [оптимальна]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.<br /><br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Общая ошибка: неправильное событие OnResize===<br />
<br />
Не помещайте ''весь'' свой код изменения размера [элементов управления] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще [будет] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.<br />
<br />
===Общая ошибка: Width вместо ClientWidth, AdjustClientRect===<br />
<br />
[Свойство] Width - это размер [элемента управления], включая рамку. [Свойство] ClientWidth - это внутренняя ширина [элемента управления] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Пользовательские элементы управления=<br />
<br />
Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''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.<br />
<br />
'''ChangeBounds''' вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью [механизма разметки] LCL. SetBounds вызывает внутри [себя] ChangeBounds с KeepBase=false, в то время как [механизма разметки] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.<br />
<br />
'''DoSetBounds''' - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, [потому что] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.<br />
<br />
'''DoAdjustClientRectChange''' вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.<br />
<br />
'''WMSize''' существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.<br />
<br />
==AdjustClientRect==<br />
<br />
Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на [свойства] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный [механизм] привязки (например, установку).<br />
<br />
Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод '''AdjustClientRect''':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect и выравнивание===<br />
<br />
AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями [автоматической установки] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством [изменения величины] зазора:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
[Кнопка] Button1 на скриншоте была создана со [значением] Align=alClient. Также [был] задействован [свойство панели] ChildSizing.Layout.<br />
<br />
Когда [дочерний элемент] привязывается к родительскому элементу через [свойство] AnchorSides, также используется параметр AdjustClientRect:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к [значению] Top, Button1.Top будет 3px (3 плюс 0).<br />
<br />
==Собственный AutoSize==<br />
<br />
Когда параметру ''AutoSize'' установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.<br />
<br />
===Предпочтительный размер===<br />
<br />
Новый размер выбирается [механизмом] LCL через [метод] '''GetPreferredSize''', который вызывает '''CalculatePreferredSize''' [и] который можно переопределить. Например, давайте опишем '''TQuadrat''', который является [наследником] TShape, но его высота должна быть равна его ширине:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
Метод CalculatePreferredSize получает два переменных параметра: ''PreferredWidth'' и ''PreferredHeight''. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает [свойство] ''PreferredHeight'' в текущее [свойство] ''Width''. Логический параметр ''WithThemeSpace'' устарел и всегда [равен] false.<br />
<br />
'''Важно''': [метод] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать [механизм] autosize. Это создаст [бесконечный] цикл.<br />
<br />
Вычисление ''PreferredWidth/Height'' может быть дорогостоящей [операцией]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана [процедура] ''InvalidatePreferredSize''. В нашем примере ''PreferredHeight'' зависит от [параметра] ''Width'', поэтому мы должны [вызвать] перерисовку [элемента управления], когда изменяется [параметр] ''Width'':<br />
<br />
<syntaxhighlight lang="pascal"><br />
TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
LCL автоматически активирует [механизм] autosizing'а, когда границы изменяются, поэтому пример является полным.<br />
<br />
По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение [будет] равно 0, и LCL будет сохранять текущую [величину] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle [значение] csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). Примером может служить\элемент управления LCL TPanel.<br />
<br />
===AdjustSize===<br />
<br />
Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван [механизм] автоматического изменения размера. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br /><br /><br />
<br />
=Сокращение накладных расходов с помощью [методов] DisableAutoSizing, EnableAutoSizing=<br />
<br />
Начиная с [версии] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До [версии] 0.9.28 [методы] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.<br />
<br />
Каждый раз, когда вы меняете [какое-нибудь] свойство, LCL запускает [механизм] пересчета разметки:<br />
<br />
<syntaxhighlight lang="pascal"><br />
Label1.Caption := 'A'; // первый перерасчет<br />
Label2.Caption := 'B'; // второй перерасчет</syntaxhighlight><br />
<br />
Перерасчет будет возбуждать [механизм вызова] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить [механизм] автоизменения размера:<br />
<br />
<syntaxhighlight lang="pascal"><br />
DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // нет перерасчета<br />
Label2.Caption := 'B'; // нет перерасчета<br />
finally<br />
EnableAutoSizing; // однократный перерасчет<br />
end;</syntaxhighlight><br />
<br />
Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) [вызова] DisableAutoSizing.<br />
<br />
Начиная с [версии Lazarus] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для '''всех''' элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы<br />
try<br />
// удаляем элементы ...<br />
// добавляем, тасуем элементы ...<br />
// меняем заголовки элементов ...<br />
finally<br />
EnableAutoSizing; // перерасчет<br />
end;<br />
end;</syntaxhighlight><br />
<br />
К чему все это: вам не нужно [ни о чем] заботиться. Просто вызовите Disable/EnableAutoSizing.<br />
<br />
{{Note| вот так неправильно: <br />
<br /><br /><br />
<syntaxhighlight lang="pascal"><br />
Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный <br />
//счетчик ссылок на автоизменение размера<br />
</syntaxhighlight>}}<br />
<br />
==DisableAutoSizing и границы формы==<br />
<br />
DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы [можете] встретить в системах Linux.<br />
Каждый раз, когда форма изменяет размер или перемещается, [ее] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными [для отображения].<br />
<br />
<br />
----<br />
[[User:Zoltanleo|Прим.перев.]]: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши. <br />
----<br />
<br /><br /><br />
<br />
=Пример: панель кнопок=<br />
<br />
В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка ''Help'' слева и кнопки ''Ok'' и ''Cancel'' справа. Мы хотим, чтобы панель была внизу формы, заполняя всю [ее] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.<br />
<br />
Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Установите в свойстве ''Kind'' BitBtns [соответственные] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways[("отображать всегда")], чтобы увидеть их на своей платформе. Установите для свойства ''AutoSize'' [кнопок] BitBtn значение ''True'', которое уменьшит/увеличит [ширину] кнопок для идеального заполнения [их] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Установите свойство ''Align ' кнопки справки в ''alLeft'' и установите для остальных двух кнопок свойство ''Align'' в ''alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
Высота панели по-прежнему фиксирована. Теперь установите для панели свойство ''AutoSize'' в ''True''. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
Кнопка ''Ok'' имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки ''Constraints.MinWidth'' в значение ''75''. Теперь кнопка несколько расширится.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' в значение ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Наконец, очистите ''Caption'' панели и установите ее 'BevelOuter'' в ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br /><br /><br />
<br />
=Прокрутка=<br />
<br />
Некоторые элементы управления LCL, такие как ''TScrollBox'', ''TForm' и ''TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка '''TScrollingWinControl'''.<br />
<br />
Прокручиваемая '''логическая клиентская область''' элемента управления может быть больше, чем '''видимая клиентская область'''. '''Видимая клиентская область''' - это '''ClientRect'''. Она всегда начинается с [координат] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
'''Логическая клиентская область''' определяется методом '''GetLogicalClientRect'''. По умолчанию она совпадает с ''ClientRect''. Когда дочерний элемент управления привязан к правой стороне, он использует ''логическую клиентская область''. ''TScrollingWinControl'' переопределяет этот метод и возвращает ''Range''[(диапазон)] полос прокрутки, если [логическая клиентская область] больше, чем ''ClientRect''. '''Range''' можно установить вручную или автоматически с помощью ''AutoScroll=true''. Пример для ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.<br />
<br />
Теперь панель сжата, так что ClientWidth становится меньше 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.<br />
<br />
==Позиция прокрутки==<br />
<br />
Изменение положения полосы прокрутки не изменяет [значения] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, [а также] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.<br />
<br />
==Прокрутка и автомасштаб==<br />
<br />
Когда AutoSize=true, LCL расширяет [родительский] элемент управления [так], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если [родительский] элемент управления не может быть расширен, то (только) [тогда проявляется] вторичное действие AutoSize: перемещение дочерних элементов управления.<br />
<br />
=Пристыковка=<br />
<br />
Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].<br />
<br />
=Splitter=<br />
<br />
См. [[TSplitter/ru|TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap'''[(перенос слов)] изменяет поведение предпочтительного размера [компонента] label. '''WordWrap=true''' требует, чтобы [величина] Width [компонента] label была фиксированной, например, путем привязки левого и правого размера [компонента] label. Предпочтительная высота label затем вычисляется путем разбиения [своства] Caption на несколько строк.<br />
<br />
=Автоматическая настройка DPI и автоматическая настройка статической разметки=<br />
<br />
Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого [рабочего стола] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях [на разметку] в виде гибкой сетки. Существует выбор нескольких режимов, [которые] позволяют пересчитывать значения пикселей как в абсолютных [величинах], так и в корректируемых для DPI, или как считающихся просто частью размера формы.<br />
<br />
Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.<br />
<br />
В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого [рабочего стола] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.<br />
<br />
<tt>property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;</tt><br />
<br />
Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy<br />
<br />
<syntaxhighlight lang="pascal"><br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // Зависимость от виджетсета<br />
lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах<br />
lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,<br />
// ось x растягивается, чтобы заполнить экран и<br />
// y масштабируется, чтобы соответствовать DPI<br />
lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI<br />
);</syntaxhighlight><br />
<br />
Следующие новые методы в TControl позволяют [задать] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:<br />
<br />
<syntaxhighlight lang="pascal"><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.<br /><br /><br />
<br />
=Подробнее=<br />
<br />
<p>Многие элементы управления переопределяют ''TControl.DoAutoSize'' для выполнения фактического автомасштабирования. </p><br />
<p>ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.</p><br />
<p>Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts[(контексты устройств)], которые необходимы для вычисления таких вещей, как размер текста.</p><br />
<p>Вот почему вы всегда должны называть ''AdjustSize'' вместо <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> вызывает <var>DoAutoSize</var> умным способом.</p><br />
<p>Во время загрузки и создания дескриптора вызовы задерживаются.</p><br />
<p>Этот метод изначально делает то же самое, что и ''TWinControl.DoAutoSize''. Но поскольку <var>DoAutoSize</var> обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает <var>AdjustSize</var>.</p><br />
<br />
При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:<br />
<br />
LCL будет только автомасштабировать ''Width'' (Height), если [это свойство] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:<br />
*левая и правая стороны привязаны. Вы можете привязать стороны с [помощью] свойства '''Anchors''' или установить свойство ''Align'' в alTop, alBottom или alClient.<br />
*[параметр] ''Width'' ограничен [значением] свойства ''Constraints''. [Свойство] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.<br />
<br />
То же [справедливо и для свойства] ''Height''.<br />
<br />
Новый размер рассчитывается protected-методом '''TControl.CalculatePreferredSize'''.<br />
Этот метод запрашивает виджетсет для [получения] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. [У него] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - [LCL] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому [параметр] AutoSize не влияет на TMemo.<br />
<br />
Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.<br />
<br />
Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).<br />
<br />
==Parent.AutoSize==<br />
<br />
Выше описанное [дает] простое объяснение. Реальный алгоритм [функционально] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.<br />
<br />
==Свойства / Методы==<br />
<br />
*<tt>Left</tt><br />
*<tt>Top</tt><br />
<br />
Если Parent<>nil, то [значения] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда [область] ''без'' рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi [с ним] несовместим. Left и Top могут иметь отрицательные [значения] или [быть] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000[px] или более.<br />
<br />
Когда клиентская область прокручивается, Left и Top остаются неизменными.<br />
<br />
Во время изменения размера/перемещения [элемента управления] Left и Top не всегда синхронизируются с координатами дескриптора объекта.<br />
<br />
Если Parent=nil, тогда [значения] Left, Top зависят от виджетсета и диспетчера окон. До [версии] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это [поведение] на Left, Top окна.<br />
<br />
Подсказка:<br />
Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left ''и'' Top, используйте взамен:<br />
<br />
<syntaxhighlight lang="pascal"><br />
with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br /><br />
<br />
*<tt>Width</tt><br />
*<tt>Height</tt><br />
<br />
Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения [свойства] Width и Height не всегда синхронизируются с размером дескриптора объекта.<br />
<br /><br />
<br />
*<tt>BoundsRect</tt><br />
<br />
Аналогично <tt>Bounds(Left, Top, Width, Height)</tt>.<br />
<br />
Обычная ошибка новичка:<br />
<br />
<syntaxhighlight lang="pascal"><br />
BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка</syntaxhighlight><br /><br />
Это не [оказывает никакого] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // получаем границы<br />
r.Left := 3; // изменяем значение в стеке<br />
end; // нет изменений</syntaxhighlight><br />
<br /><br />
<br />
*<tt>ClientRect</tt><br />
<br />
Left и Top всегда равны 0,0. [Свойства] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это [то, что] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.<br />
<br /><br />
<br />
*<tt>ClientOrigin</tt><br />
<br />
Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение [о] перемещении в интерфейс. Интерфейс обрабатывает [сообщения о] перемещениях сразу или по очереди.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetClientBounds</tt><br />
<br />
Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox [параметры] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.<br />
<br /><br />
<br />
*<tt>LCLIntf.GetWindowRect</tt><br />
<br />
После вызова [этой функции] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а [значения] Right и Bottom будут экранной координатой нижне-правого пиксела.<br />
<br /><br />
<br />
*<tt>FBaseBoundsLock: integer</tt><br />
<br />
Увеличивается/уменьшается [методами] LockBaseBounds/UnlockBaseBounds.<br />
Используется для сохранения [поля] FBaseBounds во время вызова SetBounds.<br />
<br /><br />
<br />
*<tt>FBaseParentClientSize: TPoint</tt><br />
<br />
Размер Parent.ClientRect действителен для FBaseBounds.<br />
[Поля] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для [параметра выравнивания]<br />
akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.<br />
<br /><br />
<br />
*<tt>FBoundsRectForNewParent: TRect</tt><br />
<br />
При смене родителя элемента управления Дескриптор [объекта] создается [заново], и многое<br />
может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.<br />
<br /><br />
<br />
*<tt>fLastAlignedBounds: TRect</tt><br />
<br />
Для получения пояснения см. <tt>TControl.SetAlignedBounds</tt>.<br />
Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.<br />
<br /><br />
<br />
*<tt>FLastChangebounds: TRect</tt><br />
<br />
Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.<br />
<br /><br />
<br />
*<tt>FLastDoChangeBounds: TRect</tt><br />
<br />
Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLastResizeClientHeight: integer</tt><br />
*<tt>FLastResizeClientWidth: integer</tt><br />
*<tt>FLastResizeHeight: integer</tt><br />
*<tt>FLastResizeWidth: integer</tt><br />
<br />
Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.<br />
<br /><br />
<br />
*<tt>FLoadedClientSize: TPoint</tt><br />
<br />
Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается ''SetClientWidth/SetClientHeight'' в конце загрузки.<br />
Таким образом, LCL может восстанавливать размеры (напр., при ''akRight''), используемые во время проектирование.<br />
<br /><br />
<br />
*<tt>FReadBounds: TRect</tt><br />
<br />
Аналогично <tt>FLoadedClientSiz</tt>, но для <tt>SetLeft</tt>, <tt>SetTop</tt>, <tt>SetWidth</tt>, <tt>SetHeight</tt>.<br />
<br /><br />
<br />
*<tt>procedure SetBoundsRectForNewParent(const AValue: TRect);</tt><br />
<br />
Используется для установки <tt>FBoundsRectForNewParent</tt>. См. выше.<br />
<br /><br />
<br />
*<tt>procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Как SetBounds, но без изменения размеров по умолчанию.<br />
<br /><br />
<br />
*<tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<//tt> <br />
<br />
"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.<br />
<br /><br />
<br />
*<tt>procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;</tt><br />
<br />
Фиксирует текущие границы базовых границ.<br />
<br /><br />
<br />
*<tt>procedure SetClientHeight(Value: Integer);</tt><br />
*<tt>procedure SetClientSize(Value: TPoint);</tt><br />
*<tt>procedure SetClientWidth(Value: Integer);</tt> <br />
<br />
Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.<br />
<br /><br />
<br />
*<tt>procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Это внутренний SetBounds.<br />
Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.<br />
<br /><br />
<br />
*<tt>procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;</tt><br />
<br />
Этот [метод] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.<br />
<br /><br />
<br />
*<tt>procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;</tt><br />
<br />
Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.<br />
** игнорирует вызовы, когда границы заблокированы<br />
** блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.<br /><br />
<br />
*<tt>Function GetClientOrigin: TPoint; virtual;</tt><br />
<br />
Координаты экрана Left, Top клиентской области.<br />
<br /><br />
<br />
*<tt>Function GetClientRect: TRect; virtual;</tt><br />
<br />
Размер клиентской области. (всегда Left=0, Top=0)<br />
<br /><br />
<br />
*<tt>Function GetScrolledClientRect: TRect; virtual;</tt><br />
<br />
Видимая клиентская область ClientRect.<br />
<br /><br />
<br />
*<tt>function GetChildsRect(Scrolled: boolean): TRect; virtual;</tt><br />
<br />
Возвращает клиентский прямоугольник относительно Left, Top элемента управления.<br />
Если [свойство] Scrolled [имеет значение] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).<br />
<br /><br />
<br />
*<tt>function GetClientScrollOffset: TPoint; virtual;</tt><br />
<br />
Возвращает смещение прокрутки клиентской области.<br />
<br /><br />
<br />
*<tt>function GetControlOrigin: TPoint; virtual;</tt><br />
<br />
Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране).<br />
Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. [А уж] интерфейс обрабатывает перемещаемый дескриптер сразу или в [порядке] очереди.<br />
<br /><br />
<br /><br />
<br />
=ЧаВо=<br />
<br />
==Почему [свойство] AutoSize не работает в дизайнере должным образом?==<br />
<br />
В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, [свойство] AutoSizing не обновляется при каждом изменении во время разработки.<br />
<br />
==Почему TForm.AutoSize не работает, когда что-то меняется?==<br />
<br />
См. [[Autosize_/_Layout/ru#Автомасштаб и формы|Автомасштаб и формы]]<br />
<br />
==Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?==<br />
<br />
Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// изменяем ширину элемента управления<br />
Button1.Width := Button1.Width + 10;<br />
// применяем все необходимые изменения и перерисовываем кнопку<br />
Application.ProcessMessages;<br />
// делаем много вещей, которые занимают много времени<br />
...<br />
// после выхода из OnClick LCL автоматически обрабатывает сообщения<br />
end;</syntaxhighlight><br />
<br />
==При включении привязки во время исполнения [приложения] элементы управления изменяют размеры, но не используют текущие значения. Почему?==<br />
<br />
akBottom означает: сохранить расстояние до нижней части родителя.<br />
Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом [методов] SetBounds или UpdateBaseBounds.<br />
<br />
Например:<br />
TListBox (Anchors=[akLeft,aTop]) при проектировании имеет нижний зазор в 100 пикселей.<br />
И кнопку для включения/выключения [значения] akBottom TListBox'а.<br />
Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы[, заданные] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено.<br />
Чтобы использовать текущие границы в качестве базовых, используйте:<br />
<br />
<syntaxhighlight lang="pascal"><br />
ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Установка привязки не вызывает автоматически [вызов метода] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.<br />
<br />
==Изменение размера столбцов stringgrid в событии OnResize формы не работает==<br />
<br />
[Событие] OnResize формы запускается, когда изменяются [значения] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит [непосредственно] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает[, что в таких случаях] использование [события] OnResize формы часто будет работать, но не всегда. Ярким примером [того, что использование OnResize формы] всегда терпит неудачу, является [случай], когда тема изменена, и у вас есть TStringGrid[, лежащий] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого [события] OnResize формы не запускается. Но меняется [соответственно теме] TGroupBox, поэтому TStringGrid должен быть изменен.<br />
<br />
Решение: используйте [событие] OnResize TStringGrid'а.<br />
<br />
== Почему TForm.Width равен TForm.ClientWidth?==<br />
<br />
Примечание Mattias'а:<br />
<br />
"Есть исторические и технические причины.<br />
<br />
Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, [размер рамки] доступен только после [наступления] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal.<br />
Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.<br />
<br />
Существует новое определение компилятора '''LCLRealFormBounds''' в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью <tt>LCLRealFormBounds ON</tt>. Пока что поддерживается только widgetset win32.<br />
<br />
Для всех остальных элементов управления применяются правила ClientWidth<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."<br />
<br />
==Я получаю бесконечный цикл / Как отладить автомасштабирование?==<br />
<br />
Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:<br />
<br />
===Отличия от Delphi===<br />
Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize_/_Layout/ru#Отличия от Delphi|Отличия от Delphi]]<br />
<br />
===Переопределение метода [[LCL/ru|LCL]] и запуск пересчета===<br />
<br />
Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// Это создаст бесконечный цикл<br />
InvalidatePreferredSize;<br />
<br />
// Это также создаст бесконечный цикл:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".<br />
<br />
Решение: отслеживайте изменения:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</syntaxhighlight><br />
<br />
AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.<br />
<br />
Параметр <tt>aControl: TControl</tt> поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align.<br />
Если у вас есть, например, два элемента управления A,B со [значением] Align=alLeft, то [элемент управления, который будет] нижним слева расположится левее [другого]. Если оба имеют одинаковые [значения] Left, [то] приоритетнее [элемент в] порядке создания.<br />
Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке [значения] B.Left в 0. Теперь AlignControls запускается и находит[, что у обоих элементов управления значения] A.Left=0 и B.Left=0. В обычном [случае] A [по расположению окажется в] приоритете.<br />
Чтобы [приоритетнее оказался элемент управления] B, VCL вызовет AlignControls(B,r).<br />
Таким образом, aControl является последним перемещенным.<br />
В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.<br />
<br />
Смотри <tt>TWinControl.CreateControlAlignList</tt>.<br /><br />
<br />
===OnResize/OnChangeBounds конфликтует со свойствами LCL===<br />
Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize [имеет значение] true. Если вы определяете Label1.Width в событии OnResize, LCL будет [запускать] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в [бесконечный] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===Ошибка интерфейса LCL, пользовательский виджет===<br />
Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с [опцией] <tt>-dVerboseIntfSizing</tt>. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать [об этом] LCL через Constraints[(ограничения)]. Поищите <tt>SetInterfaceConstraints</tt> в различных интерфейсах LCL и <tt>TWSControlClass.ConstraintWidth/Height</tt>.<br />
<br />
===alCustom===<br />
Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize_/_Layout/ru#alCustom|alCustom]].<br />
<br />
=См. также=<br />
<br />
* [[Docking]]<br />
* [[Anchor_Sides/ru|Anchor Sides]]</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=IDE_Window:_Breakpoints/ru&diff=155017IDE Window: Breakpoints/ru2023-01-01T19:37:00Z<p>Zoltanleo: /* Свойства точки останова */</p>
<hr />
<div>{{IDE Window: Breakpoints}}<br />
<br />
= Важно =<br />
<br />
Вы должны [[Debugger_Setup/ru|настроить отладчик]] и запустить проект для его отладки. Только тогда это окно будет полезным.<br />
<br />
<br />
{{Note|О точках наблюдения:<br />
:При настройке точки наблюдения переменная, которая будет отслеживаться, должна находиться в области видимости. (То есть программа должна быть запущена и остановлена в точке останова, где можно увидеть переменную). <br />
:По этой причине точки наблюдения действительны только до выхода из приложения и затем будут отключены. При следующей отладке они должны быть снова включены при остановке в том месте, где оно находится в области видимости.<br />
}}<br />
<br />
= Список точек останова =<br />
[[Image:Breakpoint_list.png]]<br />
<br />
Существует 3 вида точек останова:<br />
<br />
;Source Breakpoint: указывается посредством имени модуля и номера строки. Выполнение будет прервано до выполнения оператора pascal в указанной строке. Может быть установлен щелчком по выемке слева в редакторе исходного кода. <br />
;Address Breakpoint: указывается посредством адреса. Выполнение будет прервано до выполнения оператора <u>ассемблера</u> по адресу. Можно установить, щелкнув по выемке слева в окне дизассемблера. <br />
;Data/Watch Breakpoint: указывается посредством имени переменной. Выполнение будет прервано при изменении переменной. Он также может обнаружить доступ для чтения. Поддержка этого зависит от используемой платформы.<br />
<br />
== Отображение данных ==<br />
<br />
;State (состояние): Точка останова может быть включена или отключена. Если она включена, то каждый раз, когда программа достигает точки останова, проверяется условие и, если результаты верны, будут выполняться действия, заданные для точки останова.<br />
<br />
;Filename/Address (имя файла/адрес)<br>Line/Length (строка/длина): в зависимости от типа точки останова отображается следующее:<br />
:*<u>Source Breakpoint:</u><br>Filename/Address: имя файла, в котором установлена точка останова.<br> Line/Length: Номер строки, в котором задана точка останова.<br />
:*<u>Address Breakpoint:</u><br>Filename/Address: адрес точки останова. Этот тип точки останова полезен, если нет доступного исходника. <br> Line/Length: пусто<br />
:*<u>Data Breakpoint:</u><br>Filename/Address: имя переменной <br> Line/Length: покажет область ("Global" / "Declaration") и режим ("Read" / "Write" / "Read/Write")<br />
<br />
;Condition (условие): обычно достигнутая точка останова выполняет прерывание(я) выполнения кода. Когда условие определено, это условие оценивается. Если это условие оценивается как True (истина), выполняются действие(я) прерывания выполнения кода.<br />
<br />
;Action (действие): доступны следующие действия: Break(прерывание выполнения кода), Enable/Disable group(s)(включение/отключение групп(ы)), Log message(журнал сообщений), Evaluate expression(оценка выражения), Ignore/Handle Exceptions(игнорирование/обработка исключений). Полное описание этих действий можно найти в разделе [[IDE_Window:_BreakPoints/ru#.D0.A1.D0.B2.D0.BE.D0.B9.D1.81.D1.82.D0.B2.D0.B0_.D1.82.D0.BE.D1.87.D0.BA.D0.B8_.D0.BE.D1.81.D1.82.D0.B0.D0.BD.D0.BE.D0.B2.D0.B0|Свойства точки останова]] этой страницы.<br />
<br />
;Pass Count (число проходов): Сколько раз была достигнута включенная точка останова. Если для этой точки останова задано значение «Hitcount» (число срабатываний), отладчик выполнит действие(я) прерывания кода, когда будет достигнуто установленное значение Hitcount.<br />
<br />
;Group (группа): группа, к которой принадлежит эта точка останова. Опция позволяет быстро включать/отключать несколько точек останова одновременно<br />
<br />
== Интерфейс ==<br />
<br />
<br />
<u>Панель инструментов</u><br />
;[[File:debugger enable.png]] Enable/[[File:debugger_disable.png]] Disable: Включает/отключает отдельные точки останова.<br />
;[[File:laz delete.png]] Remove: Удаляет выбранные точки останова.<br />
;[[File:debugger_enable_all.png]] Enable all/[[File:debugger_disable_all.png]] Disable all: Включает/отключает все точки останова.<br />
;[[File:menu_clean.png]] Delete all: Удаляет все точки останова.<br />
;[[File:menu_environment_options.png]] Properties: Изменяет свойства текущей/выбранной точки останова.<br />
<br />
<u>Контекстное меню</u><br />
[[Image:Breakpoint_list_popmenu.png|right]]<br />
<br />
;View Source: переход к источнику (только для Source-Breakpoints).<br />
;Add: открывает диалоговое окно свойств для запрошенного типа точки останова. Расположение/адрес/переменная могут быть заданы в диалоговом окне свойств.<br />
;Enabled: устанавливает включенное состояние для отдельных точек останова.<br />
;Delete: удаляет выбранные точки останова.<br />
;Group: очищает или задает группу для точек останова. Список существующих групп будет отображен в меню. С помощью пункта "Set new group" (задать новую группу) можно создать новую группу.<br />
;Enable all/Disable all: включает/отключает все точки останова.<br />
;Delete all: удаляет все точки останова.<br />
;Disable all in same source/Enable all in same source: включает/отключает все точки останова в том же модуле, что и текущая выбранная точка останова (только для Source-Breakpoints).<br />
;Delete all in same source: удаляет все точки останова в том же модуле, что и текущая выбранная точка останова (только для Source-Breakpoints).<br />
<br />
<br style="clear:both" /><br />
<br />
= Свойства точки останова =<br />
<br />
[[File:BreakPoint_Properties.png]]<br />
<br />
;Filename/Line (Имя файла/Строка): Расположение точки останова<br />
;Condition (Условие): Точка останова будет срабатывать и выполнять указанные действия только в том случае, если условие оценивается как истинное. Это может, например, использоваться для проверки, достигла ли переменная критического значения.<br>В настоящее время допускаются только самые базовые выражения. (GDB должен уметь понимать выражение) <br><br />
:'''ПРИМЕЧАНИЕ # 1:''' В среде IDE применяется только очень ограниченное исправление синтаксиса Паскаля к условию (#48 экранируется как char с ord значением 48), поэтому он должен быть C-подобным, чтобы GDB понимал выражение, например: "i=2" or somechar='a'. Помните, что строковое (или, вернее, char) значение должно быть обрамлено в одинарные, а не в двойные кавычки. <br><br />
:'''ПРИМЕЧАНИЕ # 2:''' <s>Строки не сравниваются. Они рассматриваются как pchar, поэтому их можно сравнивать только с адресом: "str=0x5a0b40".</s> Можно сравнивать символы в строке (индекс равен нулю/pchar) (s[0]='a')and(s[1]='b')". И строки можно сравнивать как PChar (с нулевым символом в конце):$_streq(^char(MyString), "ValueToCompare") (с двойными кавычками). См .: https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Funs.html#Convenience-Funs<br />
;HitCount (Число срабатываний): Точка останова будет игнорироваться до тех пор, пока она не будет пропущена указанное количество раз. Сценарий, где это может оказатсья полезным, это когда событие вызывается изменением фокуса (между IDE и отлаженным приложением) при приостановке/возобновлении приложения. Игнорируются проходы точки останова, чтобы не изменять фокус. <br><br />
:'''ПРИМЕЧАНИЕ: # 1''' Число срабатываний начинается с 0. Поэтому, если вы хотите разбить на 10-й итерации, установите HitCount в значение 9. <br><br />
:'''ПРИМЕЧАНИЕ: # 2''' Счетчик срабатываний не сбросится до тех пор, пока вы не остановите/не начнете отладку или фактически не удалите точку останова и не установите ее заново (без необходимости останавливать/запускать сеанс отладки).<br />
;Auto continue after (Продолжить после): Это также позволяет избежать изменения фокуса между IDE и приложением. Однако он указывает на строку выполнения в редакторе исходного кода, что приводит к видимой индикации. Если время достаточно велико, окно отслеживания может быть оценено.<br />
;Group (Группа): Точкой останова может быть одна группа (только одна). Группы могут использоваться с функцией Включения/Отключения группы.<br />
;Actions (Действия): <br />
:*<u>Break (Останов):</u> Приостановить приложение. Приложение можно продолжить с помощью команды Run (Выполнить) или любой из пошаговых команд.<br />
:*<u>Enable/Disable Group (Включить/Отключить группы):</u> Включить или отключить все точки останова в данной группе (группах). Обычно устанавливаются 2 точки останова, одна для включения, другая для отключения. Точки останова в указанных группах могут быть включены (или отключены) только в том случае, если они находятся между двумя точками. Измененные точки останова могут быть в подпрограммах, которые иногда вызываются между двумя точками, но также и из других мест.<br />
:*<u>Eval Expression (Вычислить выражение):</u> Оценивает выражение и записывает значение в журнал событий отладки.<br />
:*<u>Log Message (Сообщение в журнал):</u> Записывает сообщение в журнал событий отладки.<br />
:*<u>Log Callstack (Записать стек вызовов):</u> Записывает стек вызовов в журнал событий отладки.<br />
:*<u>Take a snapshot (Сделать снимок):</u> Оценивает все Watches, Locals, Callstack, Threads и добавляет его к снимкам в диалоге Журнала.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155015Lazarus InstantSearch/ru2023-01-01T15:17:28Z<p>Zoltanleo: /* Поиск */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Настройка ==<br />
<br />
Настройка выполняется с помощью элемента 'Instant Search'(Мгновенный поиск) в группе параметров "Environment".<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
Вы должны настроить как минимум следующие вещи:<br />
* Протокол - MySQL или HTTP<br />
* Версия клиента MySQL - если вы выбрали протокол MySQL.<br />
<br />
Вы можете оставить имя хоста и порт пустыми, если вы установились на локальный компьютер с настройками по умолчанию.<br />
<br />
Проверьте соединение с помощью кнопки 'Test connection' (Проверить соединение).<br />
<br />
Если с подключением все в порядке, задайте имя используемого индекса и с помощью кнопки 'Create Index'(Создать индекс) создайте таблицу, используемую для хранения поискового индекса.<br />
<br />
Если индекс уже существует, вы будете предупреждены об этом.<br />
<br />
Когда индекс будет создан (или уже создан), диалоговое окно конфигурации проверит, какие исходные деревья существуют (обычно их нет), и предложит начать индексирование. Это делается в фоновом режиме.<br />
<br />
=== Настройка исходных деревьев ===<br />
<br />
По умолчанию InstantSearch настраивает 4 исходных дерева:<br />
* RTL : Источники RTL для FPC.<br />
* FCL : исходники пакетов FPC.<br />
* Компилятор : исходники компилятора FPC<br />
* LCL : Исходники lazarus LCL.<br />
<br />
Вы можете определить любое количество деревьев поиска, используя кнопки на панели инструментов.<br />
Вы можете использовать кнопки для обновления индексов исходного дерева.<br />
<br />
{{Note| когда вы измените исходный каталог FPC или повторно просканируете его, деревья RTL/FCL/Compiler будут переиндексированы.}}<br />
<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
4 дерева настраиваются и индексируются автоматически, а когда это происходит, в окне сообщений появляется сообщение.<br />
<br />
=== Индексация текущего (активного) проекта. ===<br />
<br />
По исходному коду текущего проекта также можно осуществлять поиск, если он был проиндексирован. Это двухэтапный процесс:<br />
<br />
* Помечаем проект как индексируемый. Это можно сделать с помощью команды ``Mark project as indexable`` (Отметить проект как индексируемый).<br />
: покажется элемент вплывающего меню в инспекторе проектов или ниже меню 'Project' в главном меню.<br />
<br />
* Тут же индексируем проект. Проект автоматически индексируется, когда вы<br />
: пометите его как индексируемый.<br />
: вручную проиндексируете проект в любое время, используя пункт меню проекта в главном меню,<br />
: или используете элемент всплывающего меню.<br />
<br />
== Поиск ==<br />
<br />
Для поиска откройте окно 'Instant search'(Мгновенный поиск) в разделе 'View'(Вид) (или нажмите {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}})<br />
<br />
Если мгновенный поиск не был настроен, вы получите предупреждение, и IDE предложит открыть диалоговое окно настройки.<br />
<br />
Диалоговое окно автоматически скопирует выделение (при условии, что оно не слишком велико и не содержит символов CR/LF) в поле редактирования поиска и выполнит поиск с его помощью.<br />
<br />
Вы можете ввести текст в поле поиска вверху.<br />
По мере ввода выводимые результаты поиска будут уточняться или расширяться.<br />
<br />
Каждое совпадение отображается с содержимым строки и именем исходного файла(номера строки).<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Дважды щелкните одну из строк, чтобы открыть файл в выбранном месте.<br />
<br />
Если у вас слишком много совпадений, вы можете отметить или снять отметку с некоторых исходных деревьев в верхней правой строке поиска.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155014Lazarus InstantSearch/ru2023-01-01T15:17:07Z<p>Zoltanleo: /* Searching */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Настройка ==<br />
<br />
Настройка выполняется с помощью элемента 'Instant Search'(Мгновенный поиск) в группе параметров "Environment".<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
Вы должны настроить как минимум следующие вещи:<br />
* Протокол - MySQL или HTTP<br />
* Версия клиента MySQL - если вы выбрали протокол MySQL.<br />
<br />
Вы можете оставить имя хоста и порт пустыми, если вы установились на локальный компьютер с настройками по умолчанию.<br />
<br />
Проверьте соединение с помощью кнопки 'Test connection' (Проверить соединение).<br />
<br />
Если с подключением все в порядке, задайте имя используемого индекса и с помощью кнопки 'Create Index'(Создать индекс) создайте таблицу, используемую для хранения поискового индекса.<br />
<br />
Если индекс уже существует, вы будете предупреждены об этом.<br />
<br />
Когда индекс будет создан (или уже создан), диалоговое окно конфигурации проверит, какие исходные деревья существуют (обычно их нет), и предложит начать индексирование. Это делается в фоновом режиме.<br />
<br />
=== Настройка исходных деревьев ===<br />
<br />
По умолчанию InstantSearch настраивает 4 исходных дерева:<br />
* RTL : Источники RTL для FPC.<br />
* FCL : исходники пакетов FPC.<br />
* Компилятор : исходники компилятора FPC<br />
* LCL : Исходники lazarus LCL.<br />
<br />
Вы можете определить любое количество деревьев поиска, используя кнопки на панели инструментов.<br />
Вы можете использовать кнопки для обновления индексов исходного дерева.<br />
<br />
{{Note| когда вы измените исходный каталог FPC или повторно просканируете его, деревья RTL/FCL/Compiler будут переиндексированы.}}<br />
<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
4 дерева настраиваются и индексируются автоматически, а когда это происходит, в окне сообщений появляется сообщение.<br />
<br />
=== Индексация текущего (активного) проекта. ===<br />
<br />
По исходному коду текущего проекта также можно осуществлять поиск, если он был проиндексирован. Это двухэтапный процесс:<br />
<br />
* Помечаем проект как индексируемый. Это можно сделать с помощью команды ``Mark project as indexable`` (Отметить проект как индексируемый).<br />
: покажется элемент вплывающего меню в инспекторе проектов или ниже меню 'Project' в главном меню.<br />
<br />
* Тут же индексируем проект. Проект автоматически индексируется, когда вы<br />
: пометите его как индексируемый.<br />
: вручную проиндексируете проект в любое время, используя пункт меню проекта в главном меню,<br />
: или используете элемент всплывающего меню.<br />
<br />
== Поиск ==<br />
<br />
Для поиска откройте окно 'Instant search'(Мгновенный поиск) в разделе 'View'(Вид) (или нажмите {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}})<br />
<br />
Если мгновенный поиск не был настроен, вы получите предупреждение, и IDE предложит открыть диалоговое окно настройки.<br />
<br />
Диалоговое окно автоматически скопирует выделение (при условии, что оно не слишком велико и не содержит символов CR/LF) в поле редактирования поиска и выполнит поиск с его помощью.<br />
<br />
Вы можете ввести текст в поле поиска вверху.<br />
По мере ввода выводимые результаты поиска будут уточняться или расширяться.<br />
<br />
Каждое совпадение отображается с содержимым строки и именем исходного файла + номера строки.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Дважды щелкните одну из строк, чтобы открыть файл в выбранном месте.<br />
<br />
Если у вас слишком много совпадений, вы можете отметить или снять отметку с некоторых исходных деревьев в верхней правой строке поиска.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155013Lazarus InstantSearch/ru2023-01-01T15:13:12Z<p>Zoltanleo: /* Indexing the current (active) project. */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Настройка ==<br />
<br />
Настройка выполняется с помощью элемента 'Instant Search'(Мгновенный поиск) в группе параметров "Environment".<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
Вы должны настроить как минимум следующие вещи:<br />
* Протокол - MySQL или HTTP<br />
* Версия клиента MySQL - если вы выбрали протокол MySQL.<br />
<br />
Вы можете оставить имя хоста и порт пустыми, если вы установились на локальный компьютер с настройками по умолчанию.<br />
<br />
Проверьте соединение с помощью кнопки 'Test connection' (Проверить соединение).<br />
<br />
Если с подключением все в порядке, задайте имя используемого индекса и с помощью кнопки 'Create Index'(Создать индекс) создайте таблицу, используемую для хранения поискового индекса.<br />
<br />
Если индекс уже существует, вы будете предупреждены об этом.<br />
<br />
Когда индекс будет создан (или уже создан), диалоговое окно конфигурации проверит, какие исходные деревья существуют (обычно их нет), и предложит начать индексирование. Это делается в фоновом режиме.<br />
<br />
=== Настройка исходных деревьев ===<br />
<br />
По умолчанию InstantSearch настраивает 4 исходных дерева:<br />
* RTL : Источники RTL для FPC.<br />
* FCL : исходники пакетов FPC.<br />
* Компилятор : исходники компилятора FPC<br />
* LCL : Исходники lazarus LCL.<br />
<br />
Вы можете определить любое количество деревьев поиска, используя кнопки на панели инструментов.<br />
Вы можете использовать кнопки для обновления индексов исходного дерева.<br />
<br />
{{Note| когда вы измените исходный каталог FPC или повторно просканируете его, деревья RTL/FCL/Compiler будут переиндексированы.}}<br />
<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
4 дерева настраиваются и индексируются автоматически, а когда это происходит, в окне сообщений появляется сообщение.<br />
<br />
=== Индексация текущего (активного) проекта. ===<br />
<br />
По исходному коду текущего проекта также можно осуществлять поиск, если он был проиндексирован. Это двухэтапный процесс:<br />
<br />
* Помечаем проект как индексируемый. Это можно сделать с помощью команды ``Mark project as indexable`` (Отметить проект как индексируемый).<br />
: покажется элемент вплывающего меню в инспекторе проектов или ниже меню 'Project' в главном меню.<br />
<br />
* Тут же индексируем проект. Проект автоматически индексируется, когда вы<br />
: пометите его как индексируемый.<br />
: вручную проиндексируете проект в любое время, используя пункт меню проекта в главном меню,<br />
: или используете элемент всплывающего меню.<br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155012Lazarus InstantSearch/ru2023-01-01T15:07:23Z<p>Zoltanleo: /* Настройка исходных деревьев */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Настройка ==<br />
<br />
Настройка выполняется с помощью элемента 'Instant Search'(Мгновенный поиск) в группе параметров "Environment".<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
Вы должны настроить как минимум следующие вещи:<br />
* Протокол - MySQL или HTTP<br />
* Версия клиента MySQL - если вы выбрали протокол MySQL.<br />
<br />
Вы можете оставить имя хоста и порт пустыми, если вы установились на локальный компьютер с настройками по умолчанию.<br />
<br />
Проверьте соединение с помощью кнопки 'Test connection' (Проверить соединение).<br />
<br />
Если с подключением все в порядке, задайте имя используемого индекса и с помощью кнопки 'Create Index'(Создать индекс) создайте таблицу, используемую для хранения поискового индекса.<br />
<br />
Если индекс уже существует, вы будете предупреждены об этом.<br />
<br />
Когда индекс будет создан (или уже создан), диалоговое окно конфигурации проверит, какие исходные деревья существуют (обычно их нет), и предложит начать индексирование. Это делается в фоновом режиме.<br />
<br />
=== Настройка исходных деревьев ===<br />
<br />
По умолчанию InstantSearch настраивает 4 исходных дерева:<br />
* RTL : Источники RTL для FPC.<br />
* FCL : исходники пакетов FPC.<br />
* Компилятор : исходники компилятора FPC<br />
* LCL : Исходники lazarus LCL.<br />
<br />
Вы можете определить любое количество деревьев поиска, используя кнопки на панели инструментов.<br />
Вы можете использовать кнопки для обновления индексов исходного дерева.<br />
<br />
{{Note| когда вы измените исходный каталог FPC или повторно просканируете его, деревья RTL/FCL/Compiler будут переиндексированы.}}<br />
<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
4 дерева настраиваются и индексируются автоматически, а когда это происходит, в окне сообщений появляется сообщение.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item.<br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155011Lazarus InstantSearch/ru2023-01-01T15:06:50Z<p>Zoltanleo: /* Настройка исходных деревьев */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Настройка ==<br />
<br />
Настройка выполняется с помощью элемента 'Instant Search'(Мгновенный поиск) в группе параметров "Environment".<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
Вы должны настроить как минимум следующие вещи:<br />
* Протокол - MySQL или HTTP<br />
* Версия клиента MySQL - если вы выбрали протокол MySQL.<br />
<br />
Вы можете оставить имя хоста и порт пустыми, если вы установились на локальный компьютер с настройками по умолчанию.<br />
<br />
Проверьте соединение с помощью кнопки 'Test connection' (Проверить соединение).<br />
<br />
Если с подключением все в порядке, задайте имя используемого индекса и с помощью кнопки 'Create Index'(Создать индекс) создайте таблицу, используемую для хранения поискового индекса.<br />
<br />
Если индекс уже существует, вы будете предупреждены об этом.<br />
<br />
Когда индекс будет создан (или уже создан), диалоговое окно конфигурации проверит, какие исходные деревья существуют (обычно их нет), и предложит начать индексирование. Это делается в фоновом режиме.<br />
<br />
=== Настройка исходных деревьев ===<br />
<br />
По умолчанию InstantSearch настраивает 4 исходных дерева:<br />
* RTL : Источники RTL для FPC.<br />
* FCL : исходники пакетов FPC.<br />
* Компилятор : исходники компилятора FPC<br />
* LCL : Исходники lazarus LCL.<br />
<br />
Вы можете определить любое количество деревьев поиска, используя кнопки на панели инструментов.<br />
Вы можете использовать кнопки для обновления индексов исходного дерева.<br />
<br />
{{Note| когда вы меняете исходный каталог FPC или повторно сканируете его, деревья RTL/FCL/Compiler будут переиндексированы.}}<br />
<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
4 дерева настраиваются и индексируются автоматически, а когда это происходит, в окне сообщений появляется сообщение.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item.<br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155010Lazarus InstantSearch/ru2023-01-01T15:06:20Z<p>Zoltanleo: /* Configuration */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Настройка ==<br />
<br />
Настройка выполняется с помощью элемента 'Instant Search'(Мгновенный поиск) в группе параметров "Environment".<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
Вы должны настроить как минимум следующие вещи:<br />
* Протокол - MySQL или HTTP<br />
* Версия клиента MySQL - если вы выбрали протокол MySQL.<br />
<br />
Вы можете оставить имя хоста и порт пустыми, если вы установились на локальный компьютер с настройками по умолчанию.<br />
<br />
Проверьте соединение с помощью кнопки 'Test connection' (Проверить соединение).<br />
<br />
Если с подключением все в порядке, задайте имя используемого индекса и с помощью кнопки 'Create Index'(Создать индекс) создайте таблицу, используемую для хранения поискового индекса.<br />
<br />
Если индекс уже существует, вы будете предупреждены об этом.<br />
<br />
Когда индекс будет создан (или уже создан), диалоговое окно конфигурации проверит, какие исходные деревья существуют (обычно их нет), и предложит начать индексирование. Это делается в фоновом режиме.<br />
<br />
=== Настройка исходных деревьев ===<br />
<br />
По умолчанию InstantSearch настраивает 4 исходных дерева:<br />
* RTL : Источники RTL для FPC.<br />
* FCL : исходники пакетов FPC.<br />
* Компилятор : исходники компилятора FPC<br />
* LCL : Исходники lazarus LCL.<br />
<br />
Вы можете определить любое количество деревьев поиска, используя кнопки на панели инструментов.<br />
Вы можете использовать кнопки для обновления индексов исходного дерева.<br />
<br />
{{Note: когда вы меняете исходный каталог FPC или повторно сканируете его, деревья RTL/FCL/Compiler будут переиндексированы.}}<br />
<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
4 дерева настраиваются и индексируются автоматически, а когда это происходит, в окне сообщений появляется сообщение.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item.<br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155009Lazarus InstantSearch/ru2023-01-01T15:00:22Z<p>Zoltanleo: /* Installation */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Установка ==<br />
<br />
=== Установка ManticoreSearch ===<br />
<br />
Прежде чем вы сможете использовать Lazarus InstantSearch, у вас должен быть установлен Manticore Search. Его можно установить локально или на другом компьютере.<br />
<br />
Чтобы задать возможность поиска посредством Manticore, см. инструкции на<br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Установка InstantSearch ===<br />
<br />
Используя меню пакетов, установите пакет '''lazinstantsearch'''. Вам нужно будет перекомпилировать IDE.<br />
<br />
=== Настройка InstantSearch ===<br />
<br />
После установки пакета, прежде чем вы сможете использовать Instantsearch, его необходимо настроить: как минимум указать местоположение поисковой системы manticore.<br />
<br />
Используя Tools - options, выберите элемент 'Instant Search' в группе параметров "Environment".<br />
<br />
== Configuration ==<br />
The configuration is done using the 'Instant Search' item under the Environment option group.<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
You must configure at least the following things:<br />
* Protocol - MySQL or HTTP<br />
* MySQL Client version - if you selected the MySQL protocol.<br />
<br />
You can leave hostname and port empty if you installed on the local machine<br />
using the default settings.<br />
<br />
Test the connection with the 'Test connection' button.<br />
<br />
If the connection is OK, set the index name to use, and use the 'Create<br />
Index' button to create the table used to store the search index.<br />
<br />
if the index already exists, you will be warned about this.<br />
<br />
When the index has been created (or was already created), <br />
the configuration dialog will check which source trees exist (normally<br />
none), and will offer to start indexing. This is done in the background.<br />
<br />
=== Configuring source trees ===<br />
<br />
By default, InstantSearch configures 4 source trees:<br />
* RTL : The RTL sources of FPC.<br />
* FCL : The packages sources of FPC.<br />
* Compiler : The FPC compiler sources <br />
* LCL : The lazarus LCL sources.<br />
<br />
You can define as much search trees as you want, using the buttons in the toolbar.<br />
You can use the buttons to refresh the indexes of a source tree.<br />
<br />
Note: when you change the source directory of FPC or rescan it, <br />
the RTL/FCL/Compiler trees will be re-indexed.<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
The 4 trees are configured and indexed automatically, a message will appear<br />
in the messages window when this happens.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item. <br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155008Lazarus InstantSearch/ru2023-01-01T14:55:47Z<p>Zoltanleo: /* Введение */</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
Пакет Instantsearch — это пакет для поиска в исходниках (или других исходниках текста).<br />
<br />
Он обеспечивает механизм поиска по мере ввода.<br />
См. пункт меню 'View - Instantsearch'(Вид — Мгновенный поиск) (или {{keypress|Ctrl}} + {{keypress|Alt}} + {{keypress|F}}).<br />
<br />
Он использует [https://manticoresearch.com/ Manticore Search] для предоставления возможностей поиска. Поиск Manticore создает индекс исходных файлов, который затем используется для предоставления вам возможностей поиска.<br />
<br />
== Installation ==<br />
<br />
=== Install ManticoreSearch ===<br />
<br />
Before you can use Lazarus InstantSearch, you must have Manticore Search<br />
installed. It can be installed locally, or on another computer.<br />
<br />
To install Manticore search, please see the instructions at <br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Install InstantSearch ===<br />
<br />
Using the package menu, install the '''lazinstantsearch''' package.<br />
You will need to recompile the IDE.<br />
<br />
=== Configure instantsearch. ===<br />
<br />
After installing the package, before you can use instantsearch, it needs to be configured: <br />
at least the location of the manticore search engine must be specified.<br />
<br />
Using Tools - options, select the 'Instant Search' item under the Environment<br />
option group.<br />
<br />
== Configuration ==<br />
The configuration is done using the 'Instant Search' item under the Environment option group.<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
You must configure at least the following things:<br />
* Protocol - MySQL or HTTP<br />
* MySQL Client version - if you selected the MySQL protocol.<br />
<br />
You can leave hostname and port empty if you installed on the local machine<br />
using the default settings.<br />
<br />
Test the connection with the 'Test connection' button.<br />
<br />
If the connection is OK, set the index name to use, and use the 'Create<br />
Index' button to create the table used to store the search index.<br />
<br />
if the index already exists, you will be warned about this.<br />
<br />
When the index has been created (or was already created), <br />
the configuration dialog will check which source trees exist (normally<br />
none), and will offer to start indexing. This is done in the background.<br />
<br />
=== Configuring source trees ===<br />
<br />
By default, InstantSearch configures 4 source trees:<br />
* RTL : The RTL sources of FPC.<br />
* FCL : The packages sources of FPC.<br />
* Compiler : The FPC compiler sources <br />
* LCL : The lazarus LCL sources.<br />
<br />
You can define as much search trees as you want, using the buttons in the toolbar.<br />
You can use the buttons to refresh the indexes of a source tree.<br />
<br />
Note: when you change the source directory of FPC or rescan it, <br />
the RTL/FCL/Compiler trees will be re-indexed.<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
The 4 trees are configured and indexed automatically, a message will appear<br />
in the messages window when this happens.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item. <br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch&diff=155007Lazarus InstantSearch2023-01-01T14:53:03Z<p>Zoltanleo: </p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Intro ==<br />
The instantsearch package is a package to search within sources (or other sources of text)<br />
<br />
It provides an as-you-type search mechanism. <br />
See the 'View - Instantsearch' menu item (or CTRL-ALT-F).<br />
<br />
It uses [https://manticoresearch.com/ Manticore Search] to provide the<br />
search capabilities. Manticore search creates an index of the source files,<br />
which it then uses to provide you with search capabilities.<br />
<br />
== Installation ==<br />
<br />
=== Install ManticoreSearch ===<br />
<br />
Before you can use Lazarus InstantSearch, you must have Manticore Search<br />
installed. It can be installed locally, or on another computer.<br />
<br />
To install Manticore search, please see the instructions at <br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Install InstantSearch ===<br />
<br />
Using the package menu, install the '''lazinstantsearch''' package.<br />
You will need to recompile the IDE. <br />
<br />
=== Configure instantsearch. ===<br />
<br />
After installing the package, before you can use instantsearch, it needs to be configured: <br />
at least the location of the manticore search engine must be specified.<br />
<br />
Using Tools - options, select the 'Instant Search' item under the Environment<br />
option group.<br />
<br />
== Configuration ==<br />
The configuration is done using the 'Instant Search' item under the Environment option group.<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
You must configure at least the following things:<br />
* Protocol - MySQL or HTTP<br />
* MySQL Client version - if you selected the MySQL protocol.<br />
<br />
You can leave hostname and port empty if you installed on the local machine<br />
using the default settings.<br />
<br />
Test the connection with the 'Test connection' button.<br />
<br />
If the connection is OK, set the index name to use, and use the 'Create<br />
Index' button to create the table used to store the search index.<br />
<br />
if the index already exists, you will be warned about this.<br />
<br />
When the index has been created (or was already created), <br />
the configuration dialog will check which source trees exist (normally<br />
none), and will offer to start indexing. This is done in the background.<br />
<br />
=== Configuring source trees ===<br />
<br />
By default, InstantSearch configures 4 source trees:<br />
* RTL : The RTL sources of FPC.<br />
* FCL : The packages sources of FPC.<br />
* Compiler : The FPC compiler sources <br />
* LCL : The lazarus LCL sources.<br />
<br />
You can define as much search trees as you want, using the buttons in the toolbar.<br />
You can use the buttons to refresh the indexes of a source tree.<br />
<br />
Note: when you change the source directory of FPC or rescan it, <br />
the RTL/FCL/Compiler trees will be re-indexed.<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
The 4 trees are configured and indexed automatically, a message will appear<br />
in the messages window when this happens.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item. <br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch/ru&diff=155006Lazarus InstantSearch/ru2023-01-01T14:52:43Z<p>Zoltanleo: Created page with "{{LanguageBar|Lazarus InstantSearch}} == Введение == The instantsearch package is a package to search within sources (or other sources of text) It provides an as-you..."</p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Введение ==<br />
The instantsearch package is a package to search within sources (or other sources of text)<br />
<br />
It provides an as-you-type search mechanism. <br />
See the 'View - Instantsearch' menu item (or CTRL-ALT-F).<br />
<br />
It uses [https://manticoresearch.com/ Manticore Search] to provide the<br />
search capabilities. Manticore search creates an index of the source files,<br />
which it then uses to provide you with search capabilities.<br />
<br />
== Installation ==<br />
<br />
=== Install ManticoreSearch ===<br />
<br />
Before you can use Lazarus InstantSearch, you must have Manticore Search<br />
installed. It can be installed locally, or on another computer.<br />
<br />
To install Manticore search, please see the instructions at <br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Install InstantSearch ===<br />
<br />
Using the package menu, install the '''lazinstantsearch''' package.<br />
You will need to recompile the IDE.<br />
<br />
=== Configure instantsearch. ===<br />
<br />
After installing the package, before you can use instantsearch, it needs to be configured: <br />
at least the location of the manticore search engine must be specified.<br />
<br />
Using Tools - options, select the 'Instant Search' item under the Environment<br />
option group.<br />
<br />
== Configuration ==<br />
The configuration is done using the 'Instant Search' item under the Environment option group.<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
You must configure at least the following things:<br />
* Protocol - MySQL or HTTP<br />
* MySQL Client version - if you selected the MySQL protocol.<br />
<br />
You can leave hostname and port empty if you installed on the local machine<br />
using the default settings.<br />
<br />
Test the connection with the 'Test connection' button.<br />
<br />
If the connection is OK, set the index name to use, and use the 'Create<br />
Index' button to create the table used to store the search index.<br />
<br />
if the index already exists, you will be warned about this.<br />
<br />
When the index has been created (or was already created), <br />
the configuration dialog will check which source trees exist (normally<br />
none), and will offer to start indexing. This is done in the background.<br />
<br />
=== Configuring source trees ===<br />
<br />
By default, InstantSearch configures 4 source trees:<br />
* RTL : The RTL sources of FPC.<br />
* FCL : The packages sources of FPC.<br />
* Compiler : The FPC compiler sources <br />
* LCL : The lazarus LCL sources.<br />
<br />
You can define as much search trees as you want, using the buttons in the toolbar.<br />
You can use the buttons to refresh the indexes of a source tree.<br />
<br />
Note: when you change the source directory of FPC or rescan it, <br />
the RTL/FCL/Compiler trees will be re-indexed.<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
The 4 trees are configured and indexed automatically, a message will appear<br />
in the messages window when this happens.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item. <br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleohttps://wiki.freepascal.org/index.php?title=Lazarus_InstantSearch&diff=155005Lazarus InstantSearch2023-01-01T14:51:59Z<p>Zoltanleo: </p>
<hr />
<div>{{LanguageBar|Lazarus InstantSearch}}<br />
<br />
== Intro ==<br />
The instantsearch package is a package to search within sources (or other sources of text)<br />
<br />
It provides an as-you-type search mechanism. <br />
See the 'View - Instantsearch' menu item (or CTRL-ALT-F).<br />
<br />
It uses [https://manticoresearch.com/ Manticore Search] to provide the<br />
search capabilities. Manticore search creates an index of the source files,<br />
which it then uses to provide you with search capabilities.<br />
<br />
== Installation ==<br />
<br />
=== Install ManticoreSearch ===<br />
<br />
Before you can use Lazarus InstantSearch, you must have Manticore Search<br />
installed. It can be installed locally, or on another computer.<br />
<br />
To install Manticore search, please see the instructions at <br />
[https://manual.manticoresearch.com/Installation]<br />
<br />
=== Install InstantSearch ===<br />
<br />
Using the package menu, install the '''lazinstantsearch''' package.<br />
You will need to recompile the IDE.<br />
<br />
=== Configure instantsearch. ===<br />
<br />
After installing the package, before you can use instantsearch, it needs to be configured: <br />
at least the location of the manticore search engine must be specified.<br />
<br />
Using Tools - options, select the 'Instant Search' item under the Environment<br />
option group.<br />
<br />
== Configuration ==<br />
The configuration is done using the 'Instant Search' item under the Environment option group.<br />
<br />
[[File:lazarus_instantsearch_options1.png|Options]]<br />
<br />
You must configure at least the following things:<br />
* Protocol - MySQL or HTTP<br />
* MySQL Client version - if you selected the MySQL protocol.<br />
<br />
You can leave hostname and port empty if you installed on the local machine<br />
using the default settings.<br />
<br />
Test the connection with the 'Test connection' button.<br />
<br />
If the connection is OK, set the index name to use, and use the 'Create<br />
Index' button to create the table used to store the search index.<br />
<br />
if the index already exists, you will be warned about this.<br />
<br />
When the index has been created (or was already created), <br />
the configuration dialog will check which source trees exist (normally<br />
none), and will offer to start indexing. This is done in the background.<br />
<br />
=== Configuring source trees ===<br />
<br />
By default, InstantSearch configures 4 source trees:<br />
* RTL : The RTL sources of FPC.<br />
* FCL : The packages sources of FPC.<br />
* Compiler : The FPC compiler sources <br />
* LCL : The lazarus LCL sources.<br />
<br />
You can define as much search trees as you want, using the buttons in the toolbar.<br />
You can use the buttons to refresh the indexes of a source tree.<br />
<br />
Note: when you change the source directory of FPC or rescan it, <br />
the RTL/FCL/Compiler trees will be re-indexed.<br />
<br />
[[File:lazarus_instantsearch_options2.png|Source tree definitions]]<br />
<br />
The 4 trees are configured and indexed automatically, a message will appear<br />
in the messages window when this happens.<br />
<br />
=== Indexing the current (active) project. ===<br />
The sources of the current project can also be searched, provided it has<br />
been indexed. This is a 2-step process:<br />
<br />
* Mark a project as indexable. This can be done using the ``Mark project as indexable``<br />
: popup menu item in the project inspector, or below the 'Project' menu in the main menu. <br />
<br />
* Actually index the project. The project is automatically indexed when you<br />
: mark it as indexable. <br />
: You can manually index a project at any time using the project menu item in the main menu, <br />
: or the popup item. <br />
<br />
== Searching ==<br />
To search, open the 'Instant search' window under 'View' (or press CTRL-Alt-F)<br />
<br />
If Instant Search was not not configured, you will get a warning and the IDE<br />
will offer to open the configuration dialog.<br />
<br />
The dialog will automatically copy the selection (provided it is not too big and does not contain CR/LF characters) <br />
to the search edit box and do a search with that. <br />
<br />
You can type in the search edit box at the top.<br />
As you type, the search results will be more refined or expanded.<br />
<br />
Each match is displayed with the line contents, and the filename+lineno.<br />
<br />
[[File:lazarus_instantsearch_searching.png|Searching]]<br />
<br />
Double click one of the lines to open the file at the selected location.<br />
<br />
If you have too many matches, you can check or uncheck some of the source<br />
trees in the top-right search bar.</div>Zoltanleo