Difference between revisions of "LCL Internals/ru"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category included in page template)
 
(34 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{LCL Internals}}
 
{{LCL Internals}}
  
==Other Interfaces==
+
==Другие интерфейсы==
{{Interfaces}}
+
*[[Lazarus known issues (things that will never be fixed)]] - Список проблем совместимости интерфейса
 +
*[[Win32/64 Interface]] - Интерфейс winapi для Windows 95/98/Me/2K/XP/Vista, но не для CE
 +
*[[Windows CE Interface]] - Для карманных ПК и смартфонов
 +
*[[Carbon Interface]] - Интерфейс Carbon для macOS
 +
*[[Cocoa Interface]] - Интерфейс Cocoa для macOS
 +
*[[Qt Interface]] - Интерфейс Qt4 для Unix, macOS, Windows и КПК на базе Linux
 +
*[[Qt5 Interface]] - Интерфейс Qt5 для Unix, macOS, Windows и КПК на базе Linux
 +
*[[GTK1 Interface]] - Интерфейс gtk1 для Unix, macOS (X11), Windows
 +
*[[GTK2 Interface]] - Интерфейс gtk2 для Unix, macOS (X11), Windows
 +
*[[GTK3 Interface]] - Интерфейс gtk3 для Unix, macOS (X11), Windows
 +
*[[fpGUI Interface]] - Основан на библиотеке fpGUI, которая представляет собой кроссплатформенный инструментарий, полностью написанный на Object Pascal
 +
*[[Custom Drawn Interface]] - Кроссплатформенный бэкэнд LCL, написанный полностью на Object Pascal внутри Lazarus. Интерфейс Lazarus для Android.
  
 
===Советы для конкретных платформ===
 
===Советы для конкретных платформ===
 
*[[Windows Programming Tips]] - Cоветы по программированию на desktop-Windows.
 
*[[Windows Programming Tips]] - Cоветы по программированию на desktop-Windows.
 
*[[Linux Programming Tips]] - Как выполнять определенные задачи программирования в Linux
 
*[[Linux Programming Tips]] - Как выполнять определенные задачи программирования в Linux
*[[OS X Programming Tips]] - Советы по Lazarus, полезные инструменты, команды Unix и многое другое ...
+
*[[macOS Programming Tips]] - Советы по Lazarus, полезные инструменты, команды Unix и многое другое ...
 
*[[WinCE Programming Tips]] - Использование телефонного API, отправка SMS-сообщений и многое другое ...
 
*[[WinCE Programming Tips]] - Использование телефонного API, отправка SMS-сообщений и многое другое ...
 
*[[Android Programming]] - Для Android смартфонов и планшетов
 
*[[Android Programming]] - Для Android смартфонов и планшетов
Line 20: Line 31:
 
*[[Cocoa Internals]] - Некоторая информация о внутренней работе виджета Cocoa
 
*[[Cocoa Internals]] - Некоторая информация о внутренней работе виджета Cocoa
  
== Minimum Toolkit versions ==
+
== Минимальные версии Toolkit ==
  
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! Lazarus version !! Min. FPC !! Min. Gtk 2 !! Min. Qt 4 !! Min. Windows !! Min. Windows CE !! Min. Mac OS X (Carbon) !! Min. Mac OS X (Cocoa) !! Min. Req. of LCL-CustomDrawn
+
! Lazarus version !! Мин. FPC !! Мин. Gtk 2 !! Мин. Qt 4 !! Мин. Windows !! Мин. Windows CE !! Мин. macOS (Carbon) !! Мин. macOS (Cocoa) !! Мин. треб. LCL-CustomDrawn
 
|-
 
|-
 
|0.9.24
 
|0.9.24
Line 30: Line 41:
 
|4.2+
 
|4.2+
 
|Windows 98+
 
|Windows 98+
|Recommended 4.0+
+
|Рекомендуется 4.0+
 
|10.4
 
|10.4
 
|N/A
 
|N/A
Line 40: Line 51:
 
|4.3+
 
|4.3+
 
|Windows 98+
 
|Windows 98+
|Recommended 4.0+
+
|Рекомендуется 4.0+
 
|10.4
 
|10.4
 
|N/A
 
|N/A
Line 50: Line 61:
 
|4.5+
 
|4.5+
 
|Windows 98+
 
|Windows 98+
|Recommended 4.0+
+
|Рекомендуется 4.0+
 
|10.4
 
|10.4
 
|N/A
 
|N/A
Line 60: Line 71:
 
|4.5+
 
|4.5+
 
|Windows 98+
 
|Windows 98+
|Recommended 5.0+
+
|Рекомендуется 5.0+
 
|10.4
 
|10.4
 
|N/A
 
|N/A
Line 70: Line 81:
 
|4.5+
 
|4.5+
 
|Windows 98+
 
|Windows 98+
|Recommended 5.0+
+
|Рекомендуется 5.0+
 
|10.4
 
|10.4
 
|10.6
 
|10.6
|Android 2.2+, Windows 2000+, X11, Mac OS X 10.6+
+
|Android 2.2+, Windows 2000+, X11, Mac 10.6+
 
|-
 
|-
 
|1.2.6
 
|1.2.6
Line 80: Line 91:
 
|4.5+*
 
|4.5+*
 
|Windows 98+
 
|Windows 98+
|Recommended 5.0+
+
|Рекомендуется 5.0+
 
|10.4
 
|10.4
 
|10.6
 
|10.6
|Android 2.2+, Windows 2000+, X11, Mac OS X 10.6+
+
|Android 2.2+, Windows 2000+, X11, Mac 10.6+
 
|}
 
|}
  
* - Actually 4.5.0 ...4.8.4 with pre-built Qt4Pas.dll, 4.8.5+ requires a different Qt4Pas.dll
+
* - На самом деле 4.5.0 ... 4.8.4 с предварительно собранным Qt4Pas.dll, 4.8.5+ требует другого Qt4Pas.dll
  
See Also: [[Installing_Lazarus_on_MacOS_X#Compatibility]]
+
См.также: [[Installing_Lazarus_on_MacOS_X/ru#.D0.A1.D0.BE.D0.B2.D0.BC.D0.B5.D1.81.D1.82.D0.B8.D0.BC.D0.BE.D1.81.D1.82.D1.8C|совместимость]]
  
== Internals of the LCL ==
+
== Внутренности LCL ==
  
There is the LCL, and the "interface". The LCL is the part that is platform independent, and it resides in the lazarus/lcl/ directory. This directory contains mainly class definitions. Many of the different controls are actually implemented in the lazarus/lcl/include/ directory in the various .inc files. This is to find the implementation of a specific control, TCustomMemo for example, faster (which is in custommemo.inc). Every .inc starts with a line {%MainUnit ...} to define where it is included.
+
Есть LCL и есть «интерфейс». LCL - это часть, которая не зависит от платформы и находится в каталоге lazarus/lcl/. Этот каталог содержит в основном определения классов. Многие различные элементы управления фактически реализованы в каталоге lazarus/lcl/include/ в различных файлах .inc. Это делается для того, чтобы быстрее найти реализацию определенного элемента управления, например TCustomMemo (который находится в custommemo.inc). Каждый .inc начинается со строки {% MainUnit ...}, чтобы определить, где он включен.
  
Then there is the "interface" which lives in a subdirectory of the lazarus/lcl/interfaces/ directory. The gtk interface is in gtk/, win32 in win32/, etc. They all have a Interfaces unit, which is used by the lcl and creates the main interface object. Usually the main interface object is defined in XXint.pp (win32int.pp), and implemented in various inc files, XXobject.inc, for the interface specific methods, XXwinapi.inc for winapi implementation methods, XXlistsl.inc for implementation of the stringlist used by the TComboBox, TListBox, and other such controls, XXcallback.inc for handling of widget events and taking appropriate action to notify the LCL.
+
Также есть «интерфейс», который находится в подкаталоге каталога lazarus/lcl/interfaces/. Интерфейс gtk находится в gtk/, win32 в win32/ и т.д. Все они имеют модуль Interfaces, который используется lcl и создает основной объект интерфейса. Обычно основной интерфейсный объект определяется в XXint.pp (win32int.pp) и реализуется в различных inc-файлах, XXobject.inc - для методов, специфичных для интерфейса, XXwinapi.inc - для методов реализации winapi, XXlistsl.inc - для реализации списка строк, использующихся TComboBox, TListBox и другими подобными элементами управления, XXcallback.inc - для обработки событий виджета и принятия соответствующих действий для уведомления LCL.
  
Every control has a WidgetSetClass property which is of the 'mirror' class in the interfaces directory, for example: mirror of TCustomEdit is TWSCustomEdit, which methods are implemented by TWin32WSCustomEdit in win32wsstdctrls. This is the way the LCL communicates with the interface, and how it lets the interface do things.
+
Каждый элемент управления имеет свойство WidgetSetClass, принадлежащее "зеркальному" классу в каталоге интерфейсов, например: "зеркало" для TCustomEdit - это TWSCustomEdit, методы которого реализуются TWin32WSCustomEdit в win32wsstdctrls. Это способ, которым LCL взаимодействует с интерфейсом, и то, как он позволяет интерфейсу делать свое дело.
  
Communication of interface back to LCL is mostly done by sending messages, usually 'DeliverMessage' which calls TControl.Perform(<message_id>, wparam, lparam) with wparam and lparam being the extra info for the message.
+
Передача интерфейса обратно в LCL в основном осуществляется путем отправки сообщений, обычно 'DeliverMessage', который вызывает TControl.Perform (<message_id>, wparam, lparam), где wparam и lparam являются дополнительной информацией для сообщения.
  
=== Pie and RadialPie ===
+
=== Pie и RadialPie ===
The LCLIntf unit contains two functions to draw pie-shapes:
+
Модуль LCLIntf содержит две функции для рисования округлых фигур:
 
  function [[doc:lcl/lclintf/pie.html|Pie]](DC: HDC; x1, y1, x2, y2, sx, sy, ex, ey: Integer): Boolean;
 
  function [[doc:lcl/lclintf/pie.html|Pie]](DC: HDC; x1, y1, x2, y2, sx, sy, ex, ey: Integer): Boolean;
 
  function [[doc:lcl/lclintf/radialpie.html|RadialPie]](DC: HDC; x1, y1, x2, y2, Angle1, Angle2: Integer): Boolean;  
 
  function [[doc:lcl/lclintf/radialpie.html|RadialPie]](DC: HDC; x1, y1, x2, y2, Angle1, Angle2: Integer): Boolean;  
  
The Pie function uses a two points (sx,sy) and (ex,ey) to indicate start and end of the arc. RadialPie uses Angles to indicate start and end of the arc.
+
Функция Pie использует две точки (sx,sy) и (ex,ey) для указания начала и конца дуги. RadialPie использует углы, чтобы указать начало и конец дуги.
  
Pie calls TWidgetSet.Pie and RadialPie calls TWidgetSet.RadialPie. The default implementation of TWidgetSet.Pie is to convert the parameters to angles and to call TWidgetSet.RadialPie. TWidgetSet.RadialPie creates an array of points for the arc and calls TWidgetSet.Polygon.
+
Pie вызывает TWidgetSet.Pie, а RadialPie вызывает TWidgetSet.RadialPie. Реализация TWidgetSet.Pie по умолчанию заключается в преобразовании параметров в углы и вызове TWidgetSet.RadialPie. TWidgetSet.RadialPie создает массив точек для дуги и вызывает TWidgetSet.Polygon.
  
The win32 widgetset overrides TWidgetSet.Pie to call the Windows Pie function directly.
+
Набор виджетов win32 переопределяет TWidgetSet.Pie для непосредственного вызова функции Windows Pie.
  
{{Note| in older versions of Lazarus there existed RadialPie with angles which did the same as the current RadialPie and RadialPie, which does the same as our Pie. Those functions have been removed in Lazarus 0.9.21.}}
+
{{Note| в более старых версиях Lazarus существовал RadialPie с углами, которые делали то же, что и нынешние RadialPie и RadialPie, которые делают то же самое, что и наша [функция] Pie. Эти функции были удалены в Lazarus 0.9.21.}}
  
===Interfaces===
+
===Интерфейсы===
  
{{Interfaces}}
+
См. [[LCL_Internals/ru#.D0.94.D1.80.D1.83.D0.B3.D0.B8.D0.B5_.D0.B8.D0.BD.D1.82.D0.B5.D1.80.D1.84.D0.B5.D0.B9.D1.81.D1.8B|другие интерфейсы]]
  
===Adding a new unit to the LCL===
+
===Добавление нового модуля в LCL===
First add the new unit name into allclunits.pp.
+
Сначала добавьте новое имя модуля в allclunits.pp.
  
To make sure the unit is registered in the components palette see the files RegisterLCL.pas and pkgfileslcl.inc, they are located in lazarus/packager.
+
Чтобы убедиться, что модуль зарегистрирован в палитре компонентов, смотрите файлы RegisterLCL.pas и pkgfileslcl.inc, они расположены в lazarus/packager.
  
== How to create a new Widgetset ==
+
== Как создать новый Widgetset ==
This is a step-by-step tutorial of developing a new [[Widgetset|widgetset]]. It is based on my experience creating the basics of the new qt4 interface.
+
Это пошаговое руководство по разработке нового [[Widgetset|widgetset]]. Он основан на моем опыте создания основ нового интерфейса qt4.
  
To start with, why would someone want to add an Widgetset? The answer is to be able to port existing lazarus software to more platforms, without modifying their code.
+
Для начала, почему кто-то хочет добавить Widgetset? Ответ заключается в том, чтобы иметь возможность портировать существующее программное обеспечение Lazarus на большее количество платформ без изменения их кода.
  
Now, let´s write the widgetset. First of all, you need to have pascal bindings for the widget and know how to use it. Normally this is not hard. A few hours doing basic tutorials available on the internet should be enougth to get started. If the bindings do not exist already, you need to create them. If the tutorials are on another language, translate them to pascal and make them work.
+
Итак, давайте напишем набор виджетов. Прежде всего, вам нужно иметь паскаль привязки для виджета и знать, как его использовать. Обычно это не сложно. Несколько часов работы с основными учебными пособиями, доступными в Интернете, должны быть достаточными для начала. Если привязки еще не существуют, их нужно создать. Если учебные пособия написаны на другом языке, переведите их на паскаль и заставьте их работать.
  
Now, for Qt I utilized Den Jean qt4 bindings for pascal, and created a very basic Qt program using them:
+
Теперь для Qt я использовал привязки Den Jean qt4 для паскаля и создал очень простую программу Qt, используя их:
  
<syntaxhighlight>program qttest;
+
<syntaxhighlight lang=pascal>program qttest;
  
 
uses qt4;
 
uses qt4;
Line 148: Line 159:
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
The above project compiles and creates a qt4 program. Now we will use its code to write a new widgetset. After we are done, the lazarus program below will compile fine into a qt4 software:
+
Вышеуказанный проект компилируется и создает программу qt4. Теперь мы будем использовать его код для написания нового набора виджетов. После того, как мы закончим, приведенная ниже программа lazarus прекрасно скомпилируется в программу qt4:
  
<syntaxhighlight>program qttest;
+
<syntaxhighlight lang=pascal>program qttest;
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 165: Line 176:
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
Where the form is maintained by Lazarus IDE and designed visually.
+
где форма поддерживается Lazarus IDE и разрабатывается визуально.
  
The first thing to do on a new widgetset is add an empty skeleton for it. Very early development widgetsets, like qt and carbon, can serve as an skeleton.
+
Первое, что нужно сделать с новым набором виджетов, это добавить пустой скелет для него. Очень ранние наборы виджетов разработки, такие как qt и carbon, могут служить скелетом.
  
Looking at the files on the many widgets you can see the first file to be called by the lcl: Interfaces.pas This file just calls another called QtInt.pas or similar. QtInt.pas has the code for the TWidgetSet class, which we must implement. On an empty skeleton you can see that the class has various functions it must implement:
+
Просматривая файлы на многих виджетах, вы можете увидеть первый файл, который будет вызван lcl: Interfaces.pas. Этот файл просто вызывает другой файл с именем QtInt.pas или аналогичный. QtInt.pas содержит код для класса TWidgetSet, который мы должны реализовать. На пустом скелете вы можете видеть, что у класса есть различные функции, которые он должен реализовать:
  
<syntaxhighlight>  TQtWidgetSet = Class(TWidgetSet)
+
<syntaxhighlight lang=pascal>  TQtWidgetSet = Class(TWidgetSet)
 
   private
 
   private
 
     App: QApplicationH;
 
     App: QApplicationH;
Line 202: Line 213:
 
   end;</syntaxhighlight>
 
   end;</syntaxhighlight>
  
=== How to implement a new windowed component ===
+
=== Как реализовать новый оконный компонент ===
  
Windowed components are all descendents from TWinControl. Those controls have a Handle and thus, should be created by the Widgetset. It's easy to add new windowed components to a widgetset.
+
Все оконные компоненты являются потомками TWinControl. Эти элементы управления имеют дескриптор и поэтому должны создаваться набором виджетов. Добавлять новые оконные компоненты в набор виджетов [довольно] легко.
  
Let's say you want to add TQtWSCustomEdit to Qt Widgetset. To start with TCustomEdit is a descendent of TWinControl and is located on the StdCtrls unit.
+
Допустим, вы хотите добавить TQtWSCustomEdit в Qt Widgetset. [Нужно] начать с TCustomEdit, [который] является потомком TWinControl и находится в модуле StdCtrls.
  
Now, go to QtWSStrCtrls unit and look for the declaration of TQtWSCustomEdit.
+
Теперь перейдите к модулю QtWSStrCtrls и найдите объявление TQtWSCustomEdit.
  
<syntaxhighlight>  TQtWSCustomEdit = class(TWSCustomEdit)
+
<syntaxhighlight lang=pascal>  TQtWSCustomEdit = class(TWSCustomEdit)
 
   private
 
   private
 
   protected
 
   protected
Line 216: Line 227:
 
   end;</syntaxhighlight>
 
   end;</syntaxhighlight>
  
Add static methods that are declared on TWSCustomEdit and override them. The code should now look like this:
+
Добавьте статические методы, объявленные в TWSCustomEdit, и переопределите их. Код должен теперь выглядеть так:
  
<syntaxhighlight>  TQtWSCustomEdit = class(TWSCustomEdit)
+
<syntaxhighlight lang=pascal>  TQtWSCustomEdit = class(TWSCustomEdit)
 
   private
 
   private
 
   protected
 
   protected
Line 240: Line 251:
 
   end;</syntaxhighlight>
 
   end;</syntaxhighlight>
  
The commented part of the code are procedures you need to implement for TCustomEdit to be fully functional, but just CreateHandle and DestroyHandle should be enough for it to be show on the form and be editable, so it fits our needs in this article.
+
Закомментированная часть кода - это процедуры, которые необходимо реализовать, чтобы TCustomEdit был полностью функциональным, но достаточно просто [наличия] CreateHandle и DestroyHandle, чтобы он отображался в форме и был редактируемым, поэтому он соответствует нашим потребностям в этой статье.
  
Hit CTRL+SHIFT+C to code complete and the implement CreateHandle and DestroyHandle. In the case of Qt4 the code will be like this:
+
Нажмите {{keypress|Ctrl}}+{{keypress|Shift}}+{{keypress|C}}, чтобы завершить код и реализовать CreateHandle и DestroyHandle. В случае Qt4 код будет таким:
  
<syntaxhighlight>{ TQtWSCustomEdit }
+
<syntaxhighlight lang=pascal>{ TQtWSCustomEdit }
  
 
class function TQtWSCustomEdit.CreateHandle(const AWinControl: TWinControl;
 
class function TQtWSCustomEdit.CreateHandle(const AWinControl: TWinControl;
Line 252: Line 263:
 
   Str: WideString;
 
   Str: WideString;
 
begin
 
begin
   // Creates the widget
+
   // Создаем виджет
 
   WriteLn('Calling QTextDocument_create');
 
   WriteLn('Calling QTextDocument_create');
 
   Str := WideString((AWinControl as TCustomMemo).Lines.Text);
 
   Str := WideString((AWinControl as TCustomMemo).Lines.Text);
 
   Widget := QTextEdit_create(@Str, QWidgetH(AWinControl.Parent.Handle));
 
   Widget := QTextEdit_create(@Str, QWidgetH(AWinControl.Parent.Handle));
  
   // Sets it's initial properties
+
   //Задаем его начальные свойства
 
   QWidget_setGeometry(Widget, AWinControl.Left, AWinControl.Top,
 
   QWidget_setGeometry(Widget, AWinControl.Left, AWinControl.Top,
 
   AWinControl.Width, AWinControl.Height);
 
   AWinControl.Width, AWinControl.Height);
Line 271: Line 282:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
Now uncomment the like "RegisterWSComponent(TCustomEdit, TQtWSCustomEdit);" on the bottom of the unit and that's it!
+
Теперь раскомментируйте строку "RegisterWSComponent (TCustomEdit, TQtWSCustomEdit);" в конце модуля и ... все!
  
You can now drop a TCustomEdit on the bottom of a form and expect it to work. :^)  
+
Теперь вы можете поместить TCustomEdit внизу формы и ожидать, что он будет работать. :^)
  
=== Implementing TBitmap ===
+
=== Реализация TBitmap ===
  
To implement TBitmap it is necessary to understand TRawImage and TLazIntfImage as explained here: [[Developing with Graphics#Working with TLazIntfImage.2C TRawImage and TLazCanvas]]
+
Для реализации TBitmap необходимо понимать TRawImage и TLazIntfImage, как описано здесь: [[Developing with Graphics#Working with TLazIntfImage.2C TRawImage and TLazCanvas]]
  
So, let's say you want to compile the following code:
+
Итак, допустим, вы хотите скомпилировать следующий код:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TMyForm.HandleOnPaint(Sender: TObject);
 
procedure TMyForm.HandleOnPaint(Sender: TObject);
 
var
 
var
Line 295: Line 306:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
Below is the order on which functions from the widgetset interface are called when executing that code:
+
Ниже приведен порядок вызова функций из интерфейса widgetset при выполнении этого кода:
  
1 - BeginPaint
+
  1 - BeginPaint
  
This will be called only if the OnPaint event sends zero as the DC for the paint event
+
Это будет вызвано только, если событие OnPaint отправляет ноль в качестве DC для события рисования
  
2 - GetDC(0);
 
  
Just create a device context.
+
  2 - GetDC(0);
  
3 - TCDWidgetSet.RawImage_QueryDescription
+
Просто создает контекст устройства.
  
The default implementation of this routine is good for most widgetsets
 
  
4 - TCDWidgetSet.RawImage_CreateBitmaps
+
  3 - TCDWidgetSet.RawImage_QueryDescription
  
Here you need to create a native image object and load it from RawData.Data where the information is stored based on your description of the pixel format on item 2.
+
Реализация этой процедуры по умолчанию подходит для большинства наборов виджетов.
  
5 - CreateCompatibleDC(0)
 
  
This creates a temporary DC just to store the image, but at this point there is no information about the image so at this point this DC is really dummy
+
  4 - TCDWidgetSet.RawImage_CreateBitmaps
  
6 - SelectObject
+
Здесь вам нужно создать собственный объект изображения и загрузить его из RawData.Data, где хранится информация на основе вашего описания формата пикселя в пункте 2.
  
With the image as the object to be selected and the DC just created above as target DC.
 
  
7 - StretchMaskBlt
+
  5 - CreateCompatibleDC(0)
  
Finally the drawing function! DestDC is the DC allocated on BeginPaint.
+
Это создает временный DC только для хранения изображения, но на данный момент нет информации об изображении, поэтому в этот момент этот DC действительно фиктивный
  
8 - EndPaint
 
  
Again, not always utilized.
+
  6 - SelectObject
  
=== TBitmap.LoadFromDevice for screenshot taking ===
+
С изображением в качестве объекта, который будет выбран, и DC, созданный выше, в качестве целевого DC.
  
It is recomended that you first implement TBitmap before trying this step.
 
  
On LCL you can use the following code takes a screenshot from the entire screen and paints it on the canvas:
+
  7 - StretchMaskBlt
  
<syntaxhighlight>var
+
Наконец, функция рисования! DestDC - это DC, размещенный на BeginPaint.
 +
 
 +
 
 +
  8 - EndPaint
 +
 
 +
Опять же, не всегда используется.
 +
 
 +
=== TBitmap.LoadFromDevice для снятия скриншота ===
 +
 
 +
Рекомендуется сначала реализовать Bitmap, прежде чем делать этот шаг.
 +
 
 +
В LCL вы можете использовать следующий код, делающий скриншот со всего экрана и рисующий его на холсте:
 +
 
 +
<syntaxhighlight lang=pascal>var
 
   ScreenDC: HDC;
 
   ScreenDC: HDC;
 
   Bitmap: TBitmap;
 
   Bitmap: TBitmap;
Line 350: Line 368:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
If you already implemented TBitmap, there are only 2 new functions to be implemented for LoadFromDevice: GetDeviceSize and GetRawImageFromDevice
+
Если вы уже внедрили TBitmap, для LoadFromDevice нужно реализовать только 2 новые функции: GetDeviceSize и GetRawImageFromDevice
 
 
Below is a big trace, covering all widgetset functions being called on a OnPaint event that takes a screenshot and paints it on the screen. This trace was taken with Qt widgetset, and may have some imperfections. The Handle numbers should be used to check which object is being utilized on the functions.
 
  
 +
Ниже приведен большой трейс, охватывающий все функции набора виджетов, вызываемые для события OnPaint, которое делает снимок экрана и рисует его на экране. Эта трассировка была сделана с помощью набора виджетов Qt и может иметь некоторые недостатки. Номера дескрипторов должны использоваться для проверки того, какой объект используется в функциях.
  
[WinAPI BeginPaint] Result=-1220713544
+
<pre>[WinAPI BeginPaint] Result=-1220713544
 
 
 
[WinAPI GetClientBounds]
 
[WinAPI GetClientBounds]
 +
[WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0</pre>
  
[WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
+
<syntaxhighlight lang=pascal>//Входим в событие OnPaint
 
 
<syntaxhighlight>Enters on Paint event
 
 
 
 
Bitmap := TBitmap.Create;
 
Bitmap := TBitmap.Create;
 
try
 
try
 
   ScreenDC := GetDC(0);</syntaxhighlight>
 
   ScreenDC := GetDC(0);</syntaxhighlight>
  
[WinAPI GetDC] hWnd: 0 Result: -1220712920
+
<pre>[WinAPI GetDC] hWnd: 0 Result: -1220712920</pre>
 
 
<syntaxhighlight>    Bitmap.LoadFromDevice(ScreenDC);</syntaxhighlight>
 
 
 
[WinAPI GetDeviceSize]
 
 
 
[WinAPI GetRawImageFromDevice] SrcDC: -1220712920 SrcWidth: 0 SrcHeight: 0
 
 
 
[WinAPI CreateBitmapFromRawImage] Width:1024 Height:768 DataSize: 3145728 CreateMask: False Bitmap:-1220746696
 
 
 
[WinAPI GetObject] GDIObj: -1220746696 Result=84 ObjectType=Image
 
  
<syntaxhighlight> ReleaseDC(0, ScreenDC);</syntaxhighlight>
+
<syntaxhighlight lang=pascal>Bitmap.LoadFromDevice(ScreenDC);</syntaxhighlight>
  
[WinAPI ReleaseDC] hWnd: 0 DC: -1220712920
+
  [WinAPI GetDeviceSize]
 +
  [WinAPI GetRawImageFromDevice] SrcDC: -1220712920 SrcWidth: 0 SrcHeight: 0
 +
  [WinAPI CreateBitmapFromRawImage] Width:1024 Height:768 DataSize: 3145728 CreateMask: False Bitmap:-1220746696
 +
  [WinAPI GetObject] GDIObj: -1220746696 Result=84 ObjectType=Image
  
<syntaxhighlight>  Canvas.Draw(0, 0, Bitmap);</syntaxhighlight>
+
<syntaxhighlight lang=pascalReleaseDC(0, ScreenDC);</syntaxhighlight>
  
[WinAPI CreateCompatibleDC] DC: 0
+
<pre>[WinAPI ReleaseDC] hWnd: 0 DC: -1220712920</pre>
  
[WinAPI GetDC] hWnd: 0 Result: -1220712920
+
<syntaxhighlight lang=pascal>Canvas.Draw(0, 0, Bitmap);</syntaxhighlight>
  
[WinAPI SelectObject] DC=-1220712920 GDIObj=-1220746696 Result=0 ObjectType=Image
+
  [WinAPI CreateCompatibleDC] DC: 0
 +
  [WinAPI GetDC] hWnd: 0 Result: -1220712920
 +
  [WinAPI SelectObject] DC=-1220712920 GDIObj=-1220746696 Result=0 ObjectType=Image
 +
  [WinAPI StretchMaskBlt] DestDC:-1220713544 SrcDC:-1220712920 Image:137185120 X:0 Y:0 W:1024 H:768 XSrc:0 YSrc:0 WSrc:1024 HSrc:768
  
[WinAPI StretchMaskBlt] DestDC:-1220713544 SrcDC:-1220712920 Image:137185120 X:0 Y:0 W:1024 H:768 XSrc:0 YSrc:0 WSrc:1024 HSrc:768
+
<syntaxhighlight lang=pascal>finally
 
 
<syntaxhighlight>finally
 
 
   Bitmap.Free;
 
   Bitmap.Free;
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
[WinAPI SelectObject] DC=-1220712920 GDIObj=0 Invalid GDI Object
+
  [WinAPI SelectObject] DC=-1220712920 GDIObj=0 Invalid GDI Object
 +
  [WinAPI DeleteObject] GDIObject: -1220746696 Result=False ObjectType=Image
  
[WinAPI DeleteObject] GDIObject: -1220746696 Result=False ObjectType=Image
+
<syntaxhighlight lang=pascal>
 
+
//Здесь мы вышли из события OnPaint
<pre>
+
</syntaxhighlight>
Now exited the OnPaint event
 
</pre>
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI SetWindowOrgEx] DC: -1220713544 NewX: -152 NewY: -246
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
[WinAPI DeleteObject] GDIObject: 0
 
 
 
TWidgetSet.InitializeCriticalSection
 
 
 
TWidgetSet.EnterCriticalSection
 
 
 
[WinAPI SelectObject] DC=-1220713544 GDIObj=0 Invalid GDI Object
 
 
 
[WinAPI MoveToEx] DC:-1220713544 X:0 Y:0
 
 
 
[WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746760 Result=-1220746856 ObjectType=Brush
 
  
[WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746856 Result=-1220746856 ObjectType=Brush
+
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: -152 NewY: -246
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  [WinAPI DeleteObject] GDIObject: 0
 +
  TWidgetSet.InitializeCriticalSection
 +
  TWidgetSet.EnterCriticalSection
 +
  [WinAPI SelectObject] DC=-1220713544 GDIObj=0 Invalid GDI Object
 +
  [WinAPI MoveToEx] DC:-1220713544 X:0 Y:0
 +
  [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746760 Result=-1220746856 ObjectType=Brush
 +
  [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746856 Result=-1220746856 ObjectType=Brush
 +
  TWidgetSet.LeaveCriticalSection
 +
  [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
 +
  [WinAPI EndPaint] Handle: -1220611768 PS.HDC: -1220713544
  
TWidgetSet.LeaveCriticalSection
+
=== Реализация рисования в событии OnPaint формы или другого элемента управления ===
  
[WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
+
Для рисования в событии OnPaint формы само событие приходит из библиотеки лежащих в основе виджетов. Интерфейс LCL должен обработать это событие и создать соответствующий объект DC в обработчике событий, а затем вызвать LCLSendPaintMsg. Кроме того, следует также реализовать соответствующие методы рисования, такие как Rectangle или ExtTextOut. Также могут быть полезны процедуры, связанные с Pen(перо), Brush(кисть) и Font(шрифт).
  
[WinAPI EndPaint] Handle: -1220611768 PS.HDC: -1220713544
+
Вот обработчик события OnPaint из набора виджетов Cocoa, который показывает, как он вызывает LCLSentPaintMsg:
  
=== Implementing drawing in the OnPaint event of a form or another control ===
+
<syntaxhighlight lang=pascal>
 
 
For drawing in the OnPaint event of a form, the event itself comes from the underlaying widgetset library. The LCL interface should handle this event and create an appropriate DC object on the event handler and then call LCLSendPaintMsg. Besides that one should also implement the corresponding drawing methods, such as Rectangle or ExtTextOut. Pen, Brush and Font related routines might also be useful.
 
 
 
Here is the OnPaint event handler from the Cocoa widgetset which shows how it calls LCLSentPaintMsg:
 
 
 
<syntaxhighlight>
 
 
procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext;
 
procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext;
 
   const bounds, dirty:NSRect);
 
   const bounds, dirty:NSRect);
Line 471: Line 461:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
And a list of WinAPI routines which were called when running this event:
+
И список подпрограмм WinAPI, которые были вызваны при запуске этого события:
  
<pre>
+
  [TCocoaWidgetSet.GetDC] hWnd: 0 Result: 12400C0
[TCocoaWidgetSet.GetDC] hWnd: 0 Result: 12400C0
+
  [TLCLCommonCallback.Draw] OnPaint event started context: 1240340
[TLCLCommonCallback.Draw] OnPaint event started context: 1240340
+
  TCocoaWidgetSet.CreatePenIndirect
TCocoaWidgetSet.CreatePenIndirect
+
  TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC460
TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC460
+
  TCocoaWidgetSet.SelectObject Result: 0
TCocoaWidgetSet.SelectObject Result: 0
+
  TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EBF00
TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EBF00
+
  TCocoaWidgetSet.SelectObject Result: 0
TCocoaWidgetSet.SelectObject Result: 0
+
  [TCocoaWidgetSet.Rectangle] DC: 1240340 X1: 100 Y1: 100 X2: 200 Y2: 200
[TCocoaWidgetSet.Rectangle] DC: 1240340 X1: 100 Y1: 100 X2: 200 Y2: 200
+
  TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC4A0
TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC4A0
+
  TCocoaWidgetSet.SelectObject Result: 0
TCocoaWidgetSet.SelectObject Result: 0
+
  [TCocoaWidgetSet.GetTextExtentPoint] DC: 1240340 Str: Some text Count: 9
[TCocoaWidgetSet.GetTextExtentPoint] DC: 1240340 Str: Some text Count: 9
+
  [TCocoaWidgetSet.GetTextExtentPoint] Size: 65,17
[TCocoaWidgetSet.GetTextExtentPoint] Size: 65,17
+
  [TLCLCommonCallback.Draw] OnPaint event ended
[TLCLCommonCallback.Draw] OnPaint event ended
 
</pre>
 
  
=== Implementing TLabel ===
+
=== Внедрение TLabel ===
  
Implementing TLabel is particularly hard, despite it being such a basic component, because it requires that almost all painting be implemented. TLabel is not a windowed control, instead it depends on paint messages to be drawn directly into the form canvas.
+
Внедрить TLabel особенно сложно, несмотря на то, что он является этаким базовым компонентом, потому что это требует, чтобы была реализована почти вся отрисовка. TLabel не является оконным элементом управления, вместо этого он зависит от сообщений отрисовки, рисующих [компонент] непосредственно на холсте формы.
  
Before trying to get TLabel working it is recomended to test if drawing functions such as Rectangle work inside a form's OnPaint event.
+
Прежде чем пытаться заставить работать TLabel, рекомендуется проверить, работают ли функции рисования, такие как Rectangle, внутри события OnPaint формы.
  
Several WinAPI methods need to be implemented, particularly:
+
Необходимо реализовать несколько методов WinAPI, в частности:
  
'''Device Context Methods'''
+
'''Методы контекста устройства'''
  
 
BeginPaint, GetDC, EndPaint, ReleaseDC, CreateCompatibleDC
 
BeginPaint, GetDC, EndPaint, ReleaseDC, CreateCompatibleDC
  
see [[Device Contexts and GDI objects in the LCL interfaces]]
+
см. [[Device_Contexts_and_GDI_objects_in_the_LCL_interfaces|Контексты устройств и объекты GDI в интерфейсах LCL]]
  
'''GDI Objects Methods'''
+
'''Методы объектов GDI'''
  
 
SelectObject, DeleteObject, CreateFontIndirect, CreateFontIndirectEx
 
SelectObject, DeleteObject, CreateFontIndirect, CreateFontIndirectEx
  
'''Miscelaneous functions'''
+
'''Разные функции'''
  
 
InvalidateRect, GetClientBounds, SetWindowOrgEx
 
InvalidateRect, GetClientBounds, SetWindowOrgEx
  
'''Text drawing Methods'''
+
'''Методы рисования текста'''
  
DrawText. Instead of implementing DrawText one can also use the default TWidgetSet.DrawText, like the Carbon and Cocoa widgetsets do. But in this case it is necessary that one implements at least GetTextMetrics, GetTextExtentPoint and ExtTextOut. Without GetTextMetrics a form with a label will crash because the autosize will not be able to calculate the appropriate size for the label.
+
DrawText.</br>Вместо реализации DrawText можно также использовать стандартный TWidgetSet.DrawText, как это делают наборы виджетов Carbon и Cocoa. Но в этом случае необходимо, чтобы каждый реализовывал хотя бы GetTextMetrics, GetTextExtentPoint и ExtTextOut. Без GetTextMetrics форма с TLabel рухнет, потому что авторазмер не сможет рассчитать подходящий размер для TLabel.
  
'''Region functions to determine if the control is behind another'''
+
'''Функции Region для определения, что элемент управления находится за другим'''
  
 
CombineRgn, CreateRectRgn, GetClipRGN, RectVisible
 
CombineRgn, CreateRectRgn, GetClipRGN, RectVisible
  
  
Below is the order in which paint procedures are called on a form with only one TLabel, to better understand the painting sequence:
+
Ниже приведен порядок вызова процедур рисования в форме с одним TLabel, чтобы лучше понять последовательность рисования:
 
+
<pre>
1 - GetDC is called once on software startup with hWnd = 0
+
1 - GetDC вызывается один раз при запуске программного обеспечения с hWnd = 0
 
+
2 - Показывается форма
2 - The form is shown
+
3 - GetDC вызывается снова (это не произошло бы без TLabel). Вызывается несколько функций, связанных со шрифтами, а также DrawText с CalcRect, установленным в True, чтобы вычислить размер TLabel.
 +
4 - InvalidateRect вызывается на холсте формы
 +
5 - Управление возвращается к операционной системе до тех пор, пока из набора виджетов не придет сообщение отрисовки
 +
6 - Вызывается BeginPaint, и в этот момент будет выполнен код в событии OnPaint формы
 +
7 - DrawText вызывается снова с CalcRect установленным в false
 +
8 - Отрисовка закончена</pre>
  
3 - GetDC is called again (this wouldn't happen without the label). A
+
===Реализация видимости для форм и элементов управления и состояния окна===
few font related functions are called, as well as DrawText with
 
CalcRect set to True to calculate the size of the label.
 
  
4 - InvalidateRect is called on the form canvas
+
Код, который управляет видимостью, разделен между видимостью для форм и для элементов управления.
  
5 - Control goes back to the operating system until a paint message comes from the widgetset
+
====Видимость для форм и состояния окна====
  
6 - BeginPaint is called, and at this point code on OnPaint event of the form will be executed
+
Эта часть также управляет состоянием окна (свернуто, развернуто или нормально). Это реализовано как копия функции Windows API ShowWindow, поэтому вы должны реализовать TMyWidgetset.ShowWindow в файле mywinapi.inc. Не забудьте также добавить заголовок в файл mywinapih.inc.
  
7 - DrawText is called again with CalcRect set to false
+
Ниже приведен код, который реализует эту функцию в наборе виджетов Qt. Это должно [помочь] очень легко понять, скопировать и реализовать его на вашем собственном виджете. Вы также можете посмотреть, как это реализует Gtk. В Windows, конечно, API Windows вызывается напрямую, поэтому нет кода, на котором его можно [продемонстрировать].
  
8 - The Painting ends.
+
<syntaxhighlight lang=pascal>
 
+
{------------------------------------------------------------------------------
=== Implementing visibility for forms and controls and window state===
 
 
 
The code that controls visibility is split between visibility for forms, and for controls
 
 
 
'''Visibility for forms and window state'''
 
 
 
This part also controls the state of the window (minimized, maximized or normal). It is implemented as a copy of the Windows API function ShowWindow, so you must implemente the TMyWidgetset.ShowWindow on the file mywinapi.inc Don´t forget to also add a header to the file mywinapih.inc
 
 
 
Below is code that implements this function on the Qt widgetset. It should be very easy to understand, copy and implement on your own widgetset. You can also take a look how Gtk implements this. On Windows, the Windows API is called directly, of course, so there is no code to look at.
 
 
 
<syntaxhighlight>{------------------------------------------------------------------------------
 
 
   function ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
 
   function ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
  
Line 571: Line 551:
  
 
   case nCmdShow of
 
   case nCmdShow of
 
 
     SW_SHOW: QWidget_setVisible(Widget, True);
 
     SW_SHOW: QWidget_setVisible(Widget, True);
 
 
     SW_SHOWNORMAL: QWidget_showNormal(Widget);
 
     SW_SHOWNORMAL: QWidget_showNormal(Widget);
 
 
     SW_MINIMIZE: QWidget_setWindowState(Widget, QtWindowMinimized);
 
     SW_MINIMIZE: QWidget_setWindowState(Widget, QtWindowMinimized);
 
 
     SW_SHOWMINIMIZED: QWidget_showMinimized(Widget);
 
     SW_SHOWMINIMIZED: QWidget_showMinimized(Widget);
 
 
     SW_SHOWMAXIMIZED: QWidget_showMaximized(Widget);
 
     SW_SHOWMAXIMIZED: QWidget_showMaximized(Widget);
 
 
     SW_HIDE: QWidget_setVisible(Widget, False);
 
     SW_HIDE: QWidget_setVisible(Widget, False);
   
 
 
   end;
 
   end;
  
Line 589: Line 562:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
'''Visibility for controls'''
+
====Видимость для элементов управления====
  
For controls inside a form you need to implement TMyWSWinControl.ShowHide class function that resides on the TMyWSWinControl class on the file mywscontrols.pp
+
Для элементов управления внутри формы вам необходимо реализовать функцию класса TMyWSWinControl.ShowHide, которая находится в классе TMyWSWinControl в файле mywscontrols.pp
  
Remember that most controls are descendent from TWinControl, so implementing this function there will guarantee that the Visible property is implemented for all standard controls that have it. Below is a sample code for Qt widgetset.
+
Помните, что большинство элементов управления наследуются от TWinControl, поэтому реализация этой функции гарантирует, что свойство Visible реализовано для всех стандартных элементов управления, которые его имеют. Ниже приведен пример кода для набора виджетов Qt.
  
<syntaxhighlight>{------------------------------------------------------------------------------
+
<syntaxhighlight lang=pascal>
 +
{------------------------------------------------------------------------------
 
   Method: TQtWSWinControl.ShowHide
 
   Method: TQtWSWinControl.ShowHide
 
   Params:  AWinControl    - the calling object
 
   Params:  AWinControl    - the calling object
Line 614: Line 588:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
=== Implementing TStrings based Components===
+
===Реализация компонентов на основе TStrings===
Some components use a TStrings to store the information they display, such as: TCustomMemo, TCustomListBox and TCustomComboBox.
+
Некоторые компоненты используют TStrings для хранения отображаемой информации, например: TCustomMemo, TCustomListBox и TCustomComboBox.
  
To implement those it is not enougth to only implement their functions on the TQtCustomMemo class for example. One of the functions to implement will be called GetStrings, which looks like this:
+
Для их реализации недостаточно просто реализовать их функции, например, в классе TQtCustomMemo. Одна из реализуемых функций будет называться GetStrings и выглядеть следующим образом:
  
<syntaxhighlight>class function TQtWSCustomListBox.GetStrings(const ACustomListBox: TCustomListBox): TStrings;
+
<syntaxhighlight lang=pascal>class function TQtWSCustomListBox.GetStrings(const ACustomListBox: TCustomListBox): TStrings;
 
var
 
var
 
   ListWidgetH: QListWidgetH;
 
   ListWidgetH: QListWidgetH;
Line 627: Line 601:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
This function must return a TStrings descendent designed to detect when strings are added to or removed from the string list and to send this information to the widgetset to update the control. For example, the following declaration shows what TQtListStrings looks like:
+
Эта функция должна возвращать потомок TStrings, предназначенный для отслеживания, что строки добавляются или удаляются из списка, и отправлять эту информацию в набор виджетов для обновления элемента управления. Например, следующее объявление показывает, как выглядит TQtListStrings:
  
<syntaxhighlight>  TQtListStrings = class(TStrings)
+
<syntaxhighlight lang=pascal>  TQtListStrings = class(TStrings)
 
   private
 
   private
     FListChanged: Boolean; // StringList and QtListWidget out of sync
+
     FListChanged: Boolean; // StringList и QtListWidget не синхронизированы
     FStringList: TStringList; // Holds the items to show
+
     FStringList: TStringList; // Содержит пункты для показа
 
     FQtListWidget: QListWidgetH;  // Qt Widget
 
     FQtListWidget: QListWidgetH;  // Qt Widget
     FOwner: TWinControl;      // Lazarus Control Owning ListStrings
+
     FOwner: TWinControl;      // Lazarus'овский владелец элементов управления ListStrings
     FUpdating: Boolean;      // We're changing Qt Widget
+
     FUpdating: Boolean;      // Мы меняем Qt Widget
 
     procedure InternalUpdate;
 
     procedure InternalUpdate;
 
     procedure ExternalUpdate(var Astr: TStringList; Clear: Boolean = True);
 
     procedure ExternalUpdate(var Astr: TStringList; Clear: Boolean = True);
     procedure IsChanged; // OnChange triggered by program action
+
     procedure IsChanged; // событие OnChange, вызваемое действием программы
 
   protected
 
   protected
 
     function GetTextStr: string; override;
 
     function GetTextStr: string; override;
Line 659: Line 633:
 
   end;</syntaxhighlight>
 
   end;</syntaxhighlight>
  
You can see its implementation in the qtobjects.pas unit of the qt interface
+
Вы можете увидеть его реализацию в модуле qtobjects.pas интерфейса qt.
  
===Implementing Menus===
+
===Реализация меню===
  
Menus are available on the LCL to create main menus or popup menus. A TMenu is the owner of a larger menu structure with many items. Items can have subitems, and don't need extra TMenus.
+
Меню доступны на LCL для создания основных меню или всплывающих меню. TMenu является владельцем более крупной структуры меню со многими пунктами. Элементы могут иметь подэлементы, и не нуждаются в дополнительном TMenus.
  
Also remember that on LCL the handle is only created when needed and at that time all properties of the controls are already initialized. This helps a lot on widgetsets where depending on the properties of a menu item it can be of one class or another, like Qt.
+
Также помните, что в LCL дескриптор создается только при необходимости, и в это время все свойства элементов управления уже инициализированы. Это очень помогает для наборов виджетов, где в зависимости от свойств элемента меню он может принадлежать к одному или другому классу, например, Qt.
  
The following things need to be implemented in order for the menus to work:
+
Следующие вещи должны быть реализованы для того, чтобы меню работали:
  
1) All methods on the QtWSMenus unit, which will implement menu creation and modification
+
1) Все методы модуля QtWSMenus, которые будут реализовывать создание и изменение меню
  
2) function TWinCEWidgetSet.SetMenu(AWindowHandle: HWND; AMenuHandle: HMENU): Boolean; from the wincewinapi.inc file, which will implement support for a main menu associated with a window.
+
2) функция <tt>TWinCEWidgetSet.SetMenu(AWindowHandle: HWND; AMenuHandle: HMENU): Boolean;</tt> из файла wincewinapi.inc, в котором будет реализована поддержка главного меню, связанного с окном.
  
====Menu Creation Order====
+
====Порядок создания меню====
  
  
One important thing to understand when implementing menus, is in which order they are created. For example, we want to create the following menu structure:
+
При реализации меню важно понимать, в каком порядке они создаются. Например, мы хотим создать следующую структуру меню:
  
[[Image:Menu_creation_order.png]]
+
[[Image:Menu_creation_order.png|bottom]]
  
And when our application is executed, there will be a 'Creating MenuItem' message with the caption of the menu each time TQtWSMenuItem.CreateHandle is called, and a 'Creating Menu' message with the name of the menu (TMenu descendents don't have a caption), each time TQtWSMenu.CreateHandle is called.
+
И когда наше приложение будет выполнено, при каждом вызове TQtWSMenuItem.CreateHandle будет появляться сообщение 'Creating MenuItem'(Создание MenuItem) с заголовком меню и сообщение 'Creating Menu'(Создание меню) с названием меню (потомки TMenu не имеют подпись), каждый раз, когда вызывается TQtWSMenu.CreateHandle.
  
Here is the resulting output of such software:
+
Вот результирующий вывод такого программного обеспечения:
  
<pre>
+
  Creating Menu. Name: MainMenu1
Creating Menu. Name: MainMenu1
+
  Creating MenuItem: Item1          Parent=Menu.Items : TMenuItem
Creating MenuItem: Item1          Parent=Menu.Items : TMenuItem
+
  Creating MenuItem: SubItem11      Parent=Item1      : TMenuItem
Creating MenuItem: SubItem11      Parent=Item1      : TMenuItem
+
  Creating MenuItem: SubItem12      Parent=Item1      : TMenuItem
Creating MenuItem: SubItem12      Parent=Item1      : TMenuItem
+
  Creating MenuItem: SubItem13      Parent=Item1      : TMenuItem
Creating MenuItem: SubItem13      Parent=Item1      : TMenuItem
+
  Creating MenuItem: SubItem14      Parent=Item1      : TMenuItem
Creating MenuItem: SubItem14      Parent=Item1      : TMenuItem
+
  Creating MenuItem: SubSubItem141  Parent=SubItem14  : TMenuItem
Creating MenuItem: SubSubItem141  Parent=SubItem14  : TMenuItem
+
  Creating MenuItem: SubSubItem142  Parent=SubItem14  : TMenuItem
Creating MenuItem: SubSubItem142  Parent=SubItem14  : TMenuItem
+
  Creating MenuItem: SubSubItem143  Parent=SubItem14  : TMenuItem
Creating MenuItem: SubSubItem143  Parent=SubItem14  : TMenuItem
+
  Creating MenuItem: SubSubItem144  Parent=SubItem14  : TMenuItem
Creating MenuItem: SubSubItem144  Parent=SubItem14  : TMenuItem
+
  Creating MenuItem: Item2          Parent=Menu.Items : TMenuItem
Creating MenuItem: Item2          Parent=Menu.Items : TMenuItem
+
  Creating MenuItem: SubItem21      Parent=Item2      : TMenuItem
Creating MenuItem: SubItem21      Parent=Item2      : TMenuItem
+
  Creating MenuItem: SubItem22      Parent=Item2      : TMenuItem
Creating MenuItem: SubItem22      Parent=Item2      : TMenuItem
+
  Creating MenuItem: SubItem23      Parent=Item2      : TMenuItem
Creating MenuItem: SubItem23      Parent=Item2      : TMenuItem
+
  Creating MenuItem: Item3          Parent=Menu.Items : TMenuItem
Creating MenuItem: Item3          Parent=Menu.Items : TMenuItem
+
  Creating MenuItem: Item4          Parent=Menu.Items : TMenuItem
Creating MenuItem: Item4          Parent=Menu.Items : TMenuItem
 
</pre>
 
  
For all MenuItems one can use GetParentMenu to get their parent owner: Menu (TMainMenu).
+
Для всех MenuItems можно использовать GetParentMenu, чтобы получить владельца их родителя: Menu (TMainMenu).
  
===Control enabling/disabling===
+
===Включение/отключение элемента управления===
  
The current way to set control enabling/disabling is by implementing the winapi EnableWindow. This API should work generically on any control. It should enable/disable mouse and keyboard input for the specified window or control, but also mark it as uneditable by the user, by making it greyed for example.
+
Текущий способ установить включение/отключение элемента управления - реализация winapi EnableWindow. Этот API должен работать в общем на любом элементе управления. Он должен включать/отключать ввод с помощью мыши и клавиатуры для указанного окна или элемента управления, а также помечать его как недоступный для редактирования пользователем, например, делая его серым.
  
<syntaxhighlight>{------------------------------------------------------------------------------
+
<syntaxhighlight lang=pascal>{------------------------------------------------------------------------------
 
   Method:  EnableWindow
 
   Method:  EnableWindow
   Params: HWnd    - handle to window
+
   Params: HWnd    - handle to window / дескриптор окна
           BEnable -  whether to enable the window
+
           BEnable -  whether to enable the window / нужно ли включать окно
   Returns: If the window was previously disabled
+
   Returns: If the window was previously disabled / возращаемый результат: если окно было ранее отключено
  
 
   Enables or disables mouse and keyboard input to the specified window or
 
   Enables or disables mouse and keyboard input to the specified window or
   control.
+
   control / Включает или отключает ввод с клавиатуры и мыши в указанное окно или
 +
  элемент управления
 
  ------------------------------------------------------------------------------}
 
  ------------------------------------------------------------------------------}
 
function TWin32WidgetSet.EnableWindow(hWnd: HWND; bEnable: Boolean): Boolean;</syntaxhighlight>
 
function TWin32WidgetSet.EnableWindow(hWnd: HWND; bEnable: Boolean): Boolean;</syntaxhighlight>
  
===Shaped Windows===
+
===Фигурные окна===
  
Windows can be shaped based on a TBitmap or on a TRegion. The region is the visible part.
+
Окна могут быть сформированы на основе растрового изображения (TBitmap) или региона(TRegion). Регион является видимой частью
  
To implement shaped windows based on a TBitmap, implement TWSWinControl.setShape
+
Чтобы реализовать фигурные окна на основе TBitmap, реализуйте TWSWinControl.setShape
  
To implement shaped windows based on a TRegion implement LCLIntf.SetWindowRgn
+
Для реализации фигурных окон на основе TRegion реализуйте LCLIntf.SetWindowRgn
  
See also: [[LCL Tips#Creating a non-rectangular window or control]]
+
См.также: [[LCL_Tips/ru#.D0.A1.D0.BE.D0.B7.D0.B4.D0.B0.D0.BD.D0.B8.D0.B5_.D0.BD.D0.B5_.D0.BF.D1.80.D1.8F.D0.BC.D0.BE.D1.83.D0.B3.D0.BE.D0.BB.D1.8C.D0.BD.D1.8B.D1.85_.D0.BE.D0.BA.D0.BE.D0.BD_.D0.B8.D0.BB.D0.B8_.D1.8D.D0.BB.D0.B5.D0.BC.D0.B5.D0.BD.D1.82.D0.BE.D0.B2_.D1.83.D0.BF.D1.80.D0.B0.D0.B2.D0.BB.D0.B5.D0.BD.D0.B8.D1.8F|Создание не прямоугольных окон или элементов управления]]
  
===System colors===
+
===Системные цвета===
  
Some color constants are actually system colors, like clBtnFace, clForm, clWindow, etc, etc.
+
Некоторые константы цвета на самом деле являются системными цветами, такими как clBtnFace, clForm, clWindow и проч. и проч.
  
To implement support for system colors the WinAPI routine GetSysColor should be implemented:
+
Для реализации поддержки системных цветов должна быть реализована подпрограмма WinAPI GetSysColor:
  
function GetSysColor(nIndex: Integer): DWORD; override;
+
<syntaxhighlight lang=pascal>function GetSysColor(nIndex: Integer): DWORD; override;</syntaxhighlight>
  
And here is a snip of the color constants which should be supported. Check LCLType for the latest values:
+
А вот фрагмент цветовых констант, которые должны поддерживаться. Проверьте LCLType для последних значений:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
//==============================================
 
//==============================================
 
// API system Color constants  pbd
 
// API system Color constants  pbd
Line 807: Line 780:
 
===ShowMessage===
 
===ShowMessage===
  
These standard dialogs are implemented purely in the LCL in the following places:
+
Эти стандартные диалоги реализованы исключительно в LCL в следующих местах:
  
* Class TPromptDialog file lcl/include/promptdialog.inc
+
* Файл класса TPromptDialog lcl/include/promptdialog.inc
  
 
===SpinEdit===
 
===SpinEdit===
  
Both TFloatSpinEdit and TSpinEdit are implemented in the class TWSFloatSpinEdit.
+
И TFloatSpinEdit, и TSpinEdit реализованы в классе TWSFloatSpinEdit.
  
===Clipboard===
+
===Буфер обмена===
  
Clipboard support is implemented in lclintf by implementing Windows API routines. The routines are:
+
Поддержка буфера обмена реализована в lclintf путем реализации подпрограмм Windows API. Это процедуры:
  
<syntaxhighlight>function ClipboardFormatToMimeType(FormatID: TClipboardFormat): string; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
+
<syntaxhighlight lang=pascal>function ClipboardFormatToMimeType(FormatID: TClipboardFormat): string; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
 
function ClipboardGetData(ClipboardType: TClipboardType;
 
function ClipboardGetData(ClipboardType: TClipboardType;
 
   FormatID: TClipboardFormat; Stream: TStream): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
 
   FormatID: TClipboardFormat; Stream: TStream): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
// ! ClipboardGetFormats: List will be created. You must free it yourself with FreeMem(List) !
+
// ! ClipboardGetFormats: List будет создан. Вы должны освободить его самостоятельно с помощью FreeMem(List) !
 
function ClipboardGetFormats(ClipboardType: TClipboardType;
 
function ClipboardGetFormats(ClipboardType: TClipboardType;
 
   var Count: integer; var List: PClipboardFormat): boolean;  {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
 
   var Count: integer; var List: PClipboardFormat): boolean;  {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
Line 830: Line 803:
 
function ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}</syntaxhighlight>
 
function ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}</syntaxhighlight>
  
Sequence of calls during program startup:
+
Последовательность вызовов при запуске программы:
  
<syntaxhighlight>TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/bmp Result=51027040
+
<syntaxhighlight lang=pascal>TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/bmp Result=51027040
 
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/delphi.bitmap Result=0
 
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/delphi.bitmap Result=0
 
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/xpm Result=0
 
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/xpm Result=0
Line 838: Line 811:
 
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/jpeg Result=0</syntaxhighlight>
 
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/jpeg Result=0</syntaxhighlight>
  
Sequence of calls during "Caption := Clipboard.AsText":
+
Последовательность вызовов во время присвоения "Caption:= Clipboard.AsText":
  
<syntaxhighlight>TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=text/plain Result=51026976
+
<syntaxhighlight lang=pascal>TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=text/plain Result=51026976
 
TCocoaWidgetSet.ClipboardGetData ClipboardType=clipboard FormatID: 51026976</syntaxhighlight>
 
TCocoaWidgetSet.ClipboardGetData ClipboardType=clipboard FormatID: 51026976</syntaxhighlight>
  
 
===TSpeedButton===
 
===TSpeedButton===
  
This control is a TGraphicControl descendent, so it has no handle and paints itself. To retain native look, it uses the function ThemeServices.DrawElement(Details.Element=teButton) to paint itself, so implement this function to have this control working. Naturally, lot's of other DC, WindowOrg, Text Drawing and Painting routines will have to be working, as explained for TLabel.
+
Этот элемент управления является потомком TGraphicControl, поэтому он не имеет дескриптора и рисуется сам. Чтобы сохранить собственный внешний вид, он использует функцию <tt>ThemeServices.DrawElement(Details.Element=teButton)</tt> для отрисовки самого себя, поэтому реализуйте эту функцию, чтобы этот элемент управления работал. Естественно, многие другие DC, WindowOrg, рисование текста и рисование должны работать, как объяснено для TLabel.
  
===TRadioButton and TCheckButton===
+
===TRadioButton и TCheckButton===
  
'''Messages:'''
+
'''Сообщения:'''
  
Basically the Widgetset must do:
+
В основном Widgetset должен делать:
  
*Send LM_CHANGE when a radio button is unchecked/checked (LCL will take care if OnClick will be called or not)
+
*Отправлять LM_CHANGE, когда радиокнопка не отмечена/отмечена (LCL позаботится о том, будет ли вызываться OnClick или нет)
*Dont send LM_CHANGE when TWSCustomCheckBox.SetState is called (SetChecked)
+
*Не отправлять LM_CHANGE, когда вызывается TWSCustomCheckBox.SetState(SetChecked)
  
See also this bug report: http://bugs.freepascal.org/view.php?id=13939
+
См. также этот отчет об ошибке: http://bugs.freepascal.org/view.php?id=13939
  
===FullScreen support===
+
===Поддержка FullScreen===
  
FullScreen is implemented as a Windows state wsFullScreen, and as such is implemented in LCLIntf.ShowWindow where the constant SW_SHOWFULLSCREEN should be handled.
+
FullScreen(окно на весь экран без рамки - прим.перев.) реализован как состояние окна wsFullScreen и, как таковой, реализован в LCLIntf.ShowWindow, где должна обрабатываться константа SW_SHOWFULLSCREEN.
  
== An example of how the LCL interfaces work ==
+
== Пример работы интерфейсов LCL ==
  
Below is a simple example. Suppose you have developed a trayicon component. How would you implement it in the LCL to work correctly on the various different supported platforms?
+
Ниже приведен простой пример. Предположим, вы разработали компонент trayicon. Как бы вы реализовали его в LCL для правильной работы на различных поддерживаемых платформах?
 
 
You would need to generate the following files:
 
  
 +
Вам нужно будет сгенерировать следующие файлы:
 +
<pre>
 
\trayicon.pas
 
\trayicon.pas
 
 
\wstrayicon.pas
 
\wstrayicon.pas
 
 
\gtk\gtkwstrayicon.pas
 
\gtk\gtkwstrayicon.pas
 
 
\gtk\trayintf.pas
 
\gtk\trayintf.pas
 
 
\win32\win32wstrayicon.pas
 
\win32\win32wstrayicon.pas
 +
\win32\trayintf.pas</pre>
  
\win32\trayintf.pas
+
Предоставление отдельных файлов wsXXX.pas и \$(LCLWidgetType)\XXXintf.pas полностью исключает необходимость использования ifdefs. Вам нужно будет добавить в качестве пути модуля $(LCLWidgetType) в файл XXXintf.pas, чтобы инициализировать корректный файл trayintf.pas, который, в свою очередь, инициализирует корректный класс WS Tray.
 
 
 
 
Providing separate wsXXX.pas and \$(LCLWidgetType)\XXXintf.pas files avoids the need for ifdefs altogether. You will need to add as a unit path $(LCLWidgetType) to the XXXintf.pas file in order initialize the correct trayintf.pas file, which in turn initializes the correct WS Tray class.
 
  
Inside trayicon.pas you include wstrayicon. Derive your main class from an LCL class, and only use wstrayicon in its implementation. All LCL classes that communicate with a widgetset, must be derived from [[doc:/lcl/lclclasses/tlclcomponent.html|TLCLComponent]] declared in the [[doc:/lcl/lclclasses|LCLClasses]] unit.
+
Внутри trayicon.pas вы включаете wstrayicon. Извлеките свой основной класс из класса LCL и используйте только wstrayicon в его реализации. Все классы LCL, которые взаимодействуют с набором виджетов, должны быть получены из [[doc:/lcl/lclclasses/tlclcomponent.html|TLCLComponent]], объявленного в модуле [[doc:/lcl/lclclasses|LCLClasses]].
  
<syntaxhighlight>unit TrayIcon;
+
<syntaxhighlight lang=pascal>unit TrayIcon;
  
 
interface
 
interface
Line 901: Line 868:
 
procedure TTrayIcon.DoTray;
 
procedure TTrayIcon.DoTray;
 
begin
 
begin
   // Call wstrayicon
+
   // вызов wstrayicon
 
end;
 
end;
  
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
in trayintf you use gtkwstrayicon or win32trayicon depending on which
+
в trayintf вы используете gtkwstrayicon или win32trayicon, в зависимости от того, какой это файл trayintf.
trayintf file it is.
 
  
in wstrayicon you create a class like so:
+
в wstrayicon вы создаете класс следующим образом:
  
<syntaxhighlight>unit WSTrayIcon;
+
<syntaxhighlight lang=pascal>unit WSTrayIcon;
  
uses WSLCLClasses, Controls, TrayIcon; // and other things as well
+
uses WSLCLClasses, Controls, TrayIcon; // и другие модули
  
 
TWSTrayIconClass = class of TWSTrayIcon;
 
TWSTrayIconClass = class of TWSTrayIcon;
 
TWSTrayIcon = class(TWSWinControl);
 
TWSTrayIcon = class(TWSWinControl);
 
public
 
public
  class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
+
// все они должны быть виртуальными и классовыми процедурами !!
virtual; // these must all be virtual and class procedures!!
+
  class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;  
 
  class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;
 
  class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;
 
  ....
 
  ....
Line 929: Line 895:
 
procedure TWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
 
procedure TWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
 
begin
 
begin
  //do nothing
+
  //ничего не делаем
 
end;
 
end;
  
 
procedure TWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
 
procedure TWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
 
begin
 
begin
  //do nothing
+
  //ничего не делаем
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
now in gtkwstrayicon.pas do this:
+
теперь в gtkwstrayicon.pas сделайте так:
  
<syntaxhighlight>uses WSTrayIcon, WSLCLClasses, Controls, TrayIcon, gtk, gdk;
+
<syntaxhighlight lang=pascal>uses WSTrayIcon, WSLCLClasses, Controls, TrayIcon, gtk, gdk;
  
  
 
TGtkWSTrayIcon = class(TWSTrayIcon);
 
TGtkWSTrayIcon = class(TWSTrayIcon);
 
private
 
private
  class function FindSystemTray(const ATrayIcon: TCustomTrayIcon):
+
  class function FindSystemTray(const ATrayIcon: TCustomTrayIcon): TWindow; virtual;public
TWindow; virtual;
 
public
 
 
  class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
 
  class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
  class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
+
  class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
override;
+
  class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override;
  class function  CreateHandle(const AWinControl: TWinControl; const
 
AParams: TCreateParams): HWND; override;
 
 
  ....
 
  ....
 
end;
 
end;
Line 958: Line 920:
 
implementation
 
implementation
  
procedure TGtkWSTrayIcon.CreateHandle(const AWinControl: TWinControl;
+
procedure TGtkWSTrayIcon.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
const AParams: TCreateParams): HWND;
 
 
var
 
var
 
WidgetInfo: PWidgetInfo;
 
WidgetInfo: PWidgetInfo;
 
begin
 
begin
 
 
  Result := gtk_plug_new;
 
  Result := gtk_plug_new;
  WidgetInfo := CreateWidgetInfo(AWinControl, Result); // it's something
+
  WidgetInfo := CreateWidgetInfo(AWinControl, Result); // как-то так или иначе
like this anyway
 
 
  TGtkWSWincontrolClass(WidgetSetClass).SetCallbacks(AWinControl);
 
  TGtkWSWincontrolClass(WidgetSetClass).SetCallbacks(AWinControl);
  // and more stuff
+
  // и многое другое
 
end;
 
end;
  
Line 974: Line 933:
 
TCustomTrayIcon): TWindow;
 
TCustomTrayIcon): TWindow;
 
begin
 
begin
  // do something
+
  // что-то делаем
 
end;
 
end;
  
Line 983: Line 942:
 
begin
 
begin
 
  SystemTray := FindSystemTray(ATrayIcon);
 
  SystemTray := FindSystemTray(ATrayIcon);
  //do something
+
  //что-то делаем
 
end;
 
end;
  
 
procedure TGtkWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
 
procedure TGtkWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
 
begin
 
begin
  //do something
+
  //что-то делаем
 
end;
 
end;
  
Line 995: Line 954:
 
initialization
 
initialization
  
RegisterWSComponent(TCustomTrayIcon, TGtkWSTrayIcon); //this is very
+
RegisterWSComponent(TCustomTrayIcon, TGtkWSTrayIcon); //вот это очень важно!!!
important!!!
 
  
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
then finally in trayicon.pas you go as normal
+
затем наконец-то в trayicon.pas вы пишете как обычно
  
<syntaxhighlight>uses WSTrayIcon; //etc. you DON'T include GtkWSTrayIcon here!
+
<syntaxhighlight lang=pascal>uses WSTrayIcon; //и т.д., вы НЕ включаете сюда GtkWSTrayIcon!
  
 
TCustomTrayIcon = class(TWinControl)
 
TCustomTrayIcon = class(TWinControl)
Line 1,014: Line 972:
 
begin
 
begin
 
  TWSTrayIconClass(WidgetSetClass).EmbedControl(Self);
 
  TWSTrayIconClass(WidgetSetClass).EmbedControl(Self);
 
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
 
----
 
----
This document is work in progress. You can help by writing sections of this document. If you cannot find the information you are looking for in this document, please add your question to the [[Talk:LCL Internals|discussion page]]. This will help us to write documentation at a level that is neither too simple nor too complicated, and fully cover the areas that people want to know about.
+
Этот документ находится в стадии разработки. Вы можете помочь, написав разделы этого документа. Если вы не можете найти информацию, которую вы ищете в этом документе, пожалуйста, добавьте свой вопрос на [[Talk:LCL Internals|страницу обсуждения]]. Это поможет нам писать документацию на уровне, который не является ни слишком простым, ни слишком сложным, и полностью охватывает области, о которых люди хотят знать.
 
 
[[Category:LCL]]
 
[[Category:Lazarus internals]]
 

Latest revision as of 02:09, 19 February 2020

English (en) español (es) 日本語 (ja) русский (ru)

Другие интерфейсы

  • Lazarus known issues (things that will never be fixed) - Список проблем совместимости интерфейса
  • Win32/64 Interface - Интерфейс winapi для Windows 95/98/Me/2K/XP/Vista, но не для CE
  • Windows CE Interface - Для карманных ПК и смартфонов
  • Carbon Interface - Интерфейс Carbon для macOS
  • Cocoa Interface - Интерфейс Cocoa для macOS
  • Qt Interface - Интерфейс Qt4 для Unix, macOS, Windows и КПК на базе Linux
  • Qt5 Interface - Интерфейс Qt5 для Unix, macOS, Windows и КПК на базе Linux
  • GTK1 Interface - Интерфейс gtk1 для Unix, macOS (X11), Windows
  • GTK2 Interface - Интерфейс gtk2 для Unix, macOS (X11), Windows
  • GTK3 Interface - Интерфейс gtk3 для Unix, macOS (X11), Windows
  • fpGUI Interface - Основан на библиотеке fpGUI, которая представляет собой кроссплатформенный инструментарий, полностью написанный на Object Pascal
  • Custom Drawn Interface - Кроссплатформенный бэкэнд LCL, написанный полностью на Object Pascal внутри Lazarus. Интерфейс Lazarus для Android.

Советы для конкретных платформ

  • Windows Programming Tips - Cоветы по программированию на desktop-Windows.
  • Linux Programming Tips - Как выполнять определенные задачи программирования в Linux
  • macOS Programming Tips - Советы по Lazarus, полезные инструменты, команды Unix и многое другое ...
  • WinCE Programming Tips - Использование телефонного API, отправка SMS-сообщений и многое другое ...
  • Android Programming - Для Android смартфонов и планшетов
  • iPhone/iPod development - Об использовании Objective Pascal для разработки приложений для iOS

Статьи по разработке интерфейсов

  • Carbon interface internals - Если вы хотите помочь улучшить интерфейс Carbon
  • Windows CE Development Notes - Для карманных ПК и смартфонов
  • Adding a new interface - Как добавить новый интерфейс набора виджетов
  • LCL Defines - Выбор правильных опций для перекомпиляции LCL
  • LCL Internals - Некоторая информация о внутренней работе LCL
  • Cocoa Internals - Некоторая информация о внутренней работе виджета Cocoa

Минимальные версии Toolkit

Lazarus version Мин. FPC Мин. Gtk 2 Мин. Qt 4 Мин. Windows Мин. Windows CE Мин. macOS (Carbon) Мин. macOS (Cocoa) Мин. треб. LCL-CustomDrawn
0.9.24 2.2.0 2.6+ 4.2+ Windows 98+ Рекомендуется 4.0+ 10.4 N/A N/A
0.9.26 2.2.2 2.6+ 4.3+ Windows 98+ Рекомендуется 4.0+ 10.4 N/A N/A
0.9.28 2.2.4 2.8+ 4.5+ Windows 98+ Рекомендуется 4.0+ 10.4 N/A N/A
0.9.30 2.4.0 2.8+ 4.5+ Windows 98+ Рекомендуется 5.0+ 10.4 N/A N/A
0.9.31 2.4.4 2.8+ 4.5+ Windows 98+ Рекомендуется 5.0+ 10.4 10.6 Android 2.2+, Windows 2000+, X11, Mac 10.6+
1.2.6 2.6.4 2.8+ 4.5+* Windows 98+ Рекомендуется 5.0+ 10.4 10.6 Android 2.2+, Windows 2000+, X11, Mac 10.6+
  • - На самом деле 4.5.0 ... 4.8.4 с предварительно собранным Qt4Pas.dll, 4.8.5+ требует другого Qt4Pas.dll

См.также: совместимость

Внутренности LCL

Есть LCL и есть «интерфейс». LCL - это часть, которая не зависит от платформы и находится в каталоге lazarus/lcl/. Этот каталог содержит в основном определения классов. Многие различные элементы управления фактически реализованы в каталоге lazarus/lcl/include/ в различных файлах .inc. Это делается для того, чтобы быстрее найти реализацию определенного элемента управления, например TCustomMemo (который находится в custommemo.inc). Каждый .inc начинается со строки {% MainUnit ...}, чтобы определить, где он включен.

Также есть «интерфейс», который находится в подкаталоге каталога lazarus/lcl/interfaces/. Интерфейс gtk находится в gtk/, win32 в win32/ и т.д. Все они имеют модуль Interfaces, который используется lcl и создает основной объект интерфейса. Обычно основной интерфейсный объект определяется в XXint.pp (win32int.pp) и реализуется в различных inc-файлах, XXobject.inc - для методов, специфичных для интерфейса, XXwinapi.inc - для методов реализации winapi, XXlistsl.inc - для реализации списка строк, использующихся TComboBox, TListBox и другими подобными элементами управления, XXcallback.inc - для обработки событий виджета и принятия соответствующих действий для уведомления LCL.

Каждый элемент управления имеет свойство WidgetSetClass, принадлежащее "зеркальному" классу в каталоге интерфейсов, например: "зеркало" для TCustomEdit - это TWSCustomEdit, методы которого реализуются TWin32WSCustomEdit в win32wsstdctrls. Это способ, которым LCL взаимодействует с интерфейсом, и то, как он позволяет интерфейсу делать свое дело.

Передача интерфейса обратно в LCL в основном осуществляется путем отправки сообщений, обычно 'DeliverMessage', который вызывает TControl.Perform (<message_id>, wparam, lparam), где wparam и lparam являются дополнительной информацией для сообщения.

Pie и RadialPie

Модуль LCLIntf содержит две функции для рисования округлых фигур:

function Pie(DC: HDC; x1, y1, x2, y2, sx, sy, ex, ey: Integer): Boolean;
function RadialPie(DC: HDC; x1, y1, x2, y2, Angle1, Angle2: Integer): Boolean; 

Функция Pie использует две точки (sx,sy) и (ex,ey) для указания начала и конца дуги. RadialPie использует углы, чтобы указать начало и конец дуги.

Pie вызывает TWidgetSet.Pie, а RadialPie вызывает TWidgetSet.RadialPie. Реализация TWidgetSet.Pie по умолчанию заключается в преобразовании параметров в углы и вызове TWidgetSet.RadialPie. TWidgetSet.RadialPie создает массив точек для дуги и вызывает TWidgetSet.Polygon.

Набор виджетов win32 переопределяет TWidgetSet.Pie для непосредственного вызова функции Windows Pie.

Light bulb  Примечание: в более старых версиях Lazarus существовал RadialPie с углами, которые делали то же, что и нынешние RadialPie и RadialPie, которые делают то же самое, что и наша [функция] Pie. Эти функции были удалены в Lazarus 0.9.21.

Интерфейсы

См. другие интерфейсы

Добавление нового модуля в LCL

Сначала добавьте новое имя модуля в allclunits.pp.

Чтобы убедиться, что модуль зарегистрирован в палитре компонентов, смотрите файлы RegisterLCL.pas и pkgfileslcl.inc, они расположены в lazarus/packager.

Как создать новый Widgetset

Это пошаговое руководство по разработке нового widgetset. Он основан на моем опыте создания основ нового интерфейса qt4.

Для начала, почему кто-то хочет добавить Widgetset? Ответ заключается в том, чтобы иметь возможность портировать существующее программное обеспечение Lazarus на большее количество платформ без изменения их кода.

Итак, давайте напишем набор виджетов. Прежде всего, вам нужно иметь паскаль привязки для виджета и знать, как его использовать. Обычно это не сложно. Несколько часов работы с основными учебными пособиями, доступными в Интернете, должны быть достаточными для начала. Если привязки еще не существуют, их нужно создать. Если учебные пособия написаны на другом языке, переведите их на паскаль и заставьте их работать.

Теперь для Qt я использовал привязки Den Jean qt4 для паскаля и создал очень простую программу Qt, используя их:

program qttest;

uses qt4;

var
  App: QApplicationH;
  MainWindow: QMainWindowH;
begin
  App := QApplication_Create(@argc,argv);

  MainWindow := QMainWindow_Create;

  QWidget_show(MainWindow);

  QApplication_Exec;
end.

Вышеуказанный проект компилируется и создает программу qt4. Теперь мы будем использовать его код для написания нового набора виджетов. После того, как мы закончим, приведенная ниже программа lazarus прекрасно скомпилируется в программу qt4:

program qttest;

{$mode objfpc}{$H+}

uses
  Interfaces, Classes, Forms,
  { Add your units here }
  qtform;

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

где форма поддерживается Lazarus IDE и разрабатывается визуально.

Первое, что нужно сделать с новым набором виджетов, это добавить пустой скелет для него. Очень ранние наборы виджетов разработки, такие как qt и carbon, могут служить скелетом.

Просматривая файлы на многих виджетах, вы можете увидеть первый файл, который будет вызван lcl: Interfaces.pas. Этот файл просто вызывает другой файл с именем QtInt.pas или аналогичный. QtInt.pas содержит код для класса TWidgetSet, который мы должны реализовать. На пустом скелете вы можете видеть, что у класса есть различные функции, которые он должен реализовать:

  TQtWidgetSet = Class(TWidgetSet)
  private
    App: QApplicationH;
  public
    {$I qtwinapih.inc}
    {$I qtlclintfh.inc}
  public
    // Application
    procedure AppInit(var ScreenInfo: TScreenInfo); override;
    procedure AppRun(const ALoop: TApplicationMainLoop); override;
    procedure AppWaitMessage; override;
    procedure AppProcessMessages; override;
    procedure AppTerminate; override;
    procedure AppMinimize; override;
    procedure AppBringToFront; override;
  public
    constructor Create;
    destructor Destroy; override;
    function  DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override;
    procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override;
    procedure DCRedraw(CanvasHandle: HDC); override;
    procedure SetDesigning(AComponent: TComponent); override;

    function  InitHintFont(HintFont: TObject): Boolean; override;

    // create and destroy
    function CreateComponent(Sender : TObject): THandle; override; // deprecated
    function CreateTimer(Interval: integer; TimerFunc: TFNTimerProc): integer; override;
    function DestroyTimer(TimerHandle: integer): boolean; override;
  end;

Как реализовать новый оконный компонент

Все оконные компоненты являются потомками TWinControl. Эти элементы управления имеют дескриптор и поэтому должны создаваться набором виджетов. Добавлять новые оконные компоненты в набор виджетов [довольно] легко.

Допустим, вы хотите добавить TQtWSCustomEdit в Qt Widgetset. [Нужно] начать с TCustomEdit, [который] является потомком TWinControl и находится в модуле StdCtrls.

Теперь перейдите к модулю QtWSStrCtrls и найдите объявление TQtWSCustomEdit.

  TQtWSCustomEdit = class(TWSCustomEdit)
  private
  protected
  public
  end;

Добавьте статические методы, объявленные в TWSCustomEdit, и переопределите их. Код должен теперь выглядеть так:

  TQtWSCustomEdit = class(TWSCustomEdit)
  private
  protected
  public
    class function CreateHandle(const AWinControl: TWinControl;
          const AParams: TCreateParams): HWND; override;
    class procedure DestroyHandle(const AWinControl: TWinControl); override;
{    class function  GetSelStart(const ACustomEdit: TCustomEdit): integer; override;
    class function  GetSelLength(const ACustomEdit: TCustomEdit): integer; override;

    class procedure SetCharCase(const ACustomEdit: TCustomEdit; NewCase: TEditCharCase); override;
    class procedure SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); override;
    class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
    class procedure SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char); override;
    class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
    class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
    class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;

    class procedure GetPreferredSize(const AWinControl: TWinControl;
                        var PreferredWidth, PreferredHeight: integer); override;}
  end;

Закомментированная часть кода - это процедуры, которые необходимо реализовать, чтобы TCustomEdit был полностью функциональным, но достаточно просто [наличия] CreateHandle и DestroyHandle, чтобы он отображался в форме и был редактируемым, поэтому он соответствует нашим потребностям в этой статье.

Нажмите Ctrl+ Shift+C, чтобы завершить код и реализовать CreateHandle и DestroyHandle. В случае Qt4 код будет таким:

{ TQtWSCustomEdit }

class function TQtWSCustomEdit.CreateHandle(const AWinControl: TWinControl;
  const AParams: TCreateParams): HWND;
var
  Widget: QWidgetH;
  Str: WideString;
begin
  // Создаем виджет
  WriteLn('Calling QTextDocument_create');
  Str := WideString((AWinControl as TCustomMemo).Lines.Text);
  Widget := QTextEdit_create(@Str, QWidgetH(AWinControl.Parent.Handle));

  //Задаем его начальные свойства
  QWidget_setGeometry(Widget, AWinControl.Left, AWinControl.Top,
   AWinControl.Width, AWinControl.Height);

  QWidget_show(Widget);

  Result := THandle(Widget);
end;

class procedure TQtWSCustomEdit.DestroyHandle(const AWinControl: TWinControl);
begin
  QTextEdit_destroy(QTextEditH(AWinControl.Handle));
end;

Теперь раскомментируйте строку "RegisterWSComponent (TCustomEdit, TQtWSCustomEdit);" в конце модуля и ... все!

Теперь вы можете поместить TCustomEdit внизу формы и ожидать, что он будет работать. :^)

Реализация TBitmap

Для реализации TBitmap необходимо понимать TRawImage и TLazIntfImage, как описано здесь: Developing with Graphics#Working with TLazIntfImage.2C TRawImage and TLazCanvas

Итак, допустим, вы хотите скомпилировать следующий код:

procedure TMyForm.HandleOnPaint(Sender: TObject);
var
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.LoadFromFile('myfile.bmp');
    Canvas.Draw(0, 0, Bitmap);
  finally
    Bitmap.Free;
  end;
end;

Ниже приведен порядок вызова функций из интерфейса widgetset при выполнении этого кода:

 1 - BeginPaint

Это будет вызвано только, если событие OnPaint отправляет ноль в качестве DC для события рисования


 2 - GetDC(0);

Просто создает контекст устройства.


 3 - TCDWidgetSet.RawImage_QueryDescription 

Реализация этой процедуры по умолчанию подходит для большинства наборов виджетов.


 4 - TCDWidgetSet.RawImage_CreateBitmaps

Здесь вам нужно создать собственный объект изображения и загрузить его из RawData.Data, где хранится информация на основе вашего описания формата пикселя в пункте 2.


 5 - CreateCompatibleDC(0)

Это создает временный DC только для хранения изображения, но на данный момент нет информации об изображении, поэтому в этот момент этот DC действительно фиктивный


 6 - SelectObject

С изображением в качестве объекта, который будет выбран, и DC, созданный выше, в качестве целевого DC.


 7 - StretchMaskBlt

Наконец, функция рисования! DestDC - это DC, размещенный на BeginPaint.


 8 - EndPaint

Опять же, не всегда используется.

TBitmap.LoadFromDevice для снятия скриншота

Рекомендуется сначала реализовать Bitmap, прежде чем делать этот шаг.

В LCL вы можете использовать следующий код, делающий скриншот со всего экрана и рисующий его на холсте:

var
  ScreenDC: HDC;
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    ScreenDC := GetDC(0);
    Bitmap.LoadFromDevice(ScreenDC);
    ReleaseDC(0, ScreenDC);
    Canvas.Draw(0, 0, Bitmap);
  finally
    Bitmap.Free;
  end;
end;

Если вы уже внедрили TBitmap, для LoadFromDevice нужно реализовать только 2 новые функции: GetDeviceSize и GetRawImageFromDevice

Ниже приведен большой трейс, охватывающий все функции набора виджетов, вызываемые для события OnPaint, которое делает снимок экрана и рисует его на экране. Эта трассировка была сделана с помощью набора виджетов Qt и может иметь некоторые недостатки. Номера дескрипторов должны использоваться для проверки того, какой объект используется в функциях.

[WinAPI BeginPaint] Result=-1220713544
[WinAPI GetClientBounds]
[WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
//Входим в событие OnPaint
Bitmap := TBitmap.Create;
try
  ScreenDC := GetDC(0);
[WinAPI GetDC] hWnd: 0 Result: -1220712920
Bitmap.LoadFromDevice(ScreenDC);
 [WinAPI GetDeviceSize]
 [WinAPI GetRawImageFromDevice] SrcDC: -1220712920 SrcWidth: 0 SrcHeight: 0
 [WinAPI CreateBitmapFromRawImage] Width:1024 Height:768 DataSize: 3145728 CreateMask: False Bitmap:-1220746696
 [WinAPI GetObject] GDIObj: -1220746696 Result=84 ObjectType=Image
  ReleaseDC(0, ScreenDC);
[WinAPI ReleaseDC] hWnd: 0 DC: -1220712920
Canvas.Draw(0, 0, Bitmap);
 [WinAPI CreateCompatibleDC] DC: 0
 [WinAPI GetDC] hWnd: 0 Result: -1220712920
 [WinAPI SelectObject] DC=-1220712920 GDIObj=-1220746696 Result=0 ObjectType=Image
 [WinAPI StretchMaskBlt] DestDC:-1220713544 SrcDC:-1220712920 Image:137185120 X:0 Y:0 W:1024 H:768 XSrc:0 YSrc:0 WSrc:1024 HSrc:768
finally
  Bitmap.Free;
end;
 [WinAPI SelectObject] DC=-1220712920 GDIObj=0 Invalid GDI Object
 [WinAPI DeleteObject] GDIObject: -1220746696 Result=False ObjectType=Image
//Здесь мы вышли из события OnPaint
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: -152 NewY: -246
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI DeleteObject] GDIObject: 0
 [WinAPI DeleteObject] GDIObject: 0
 TWidgetSet.InitializeCriticalSection
 TWidgetSet.EnterCriticalSection
 [WinAPI SelectObject] DC=-1220713544 GDIObj=0 Invalid GDI Object
 [WinAPI MoveToEx] DC:-1220713544 X:0 Y:0
 [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746760 Result=-1220746856 ObjectType=Brush
 [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746856 Result=-1220746856 ObjectType=Brush
 TWidgetSet.LeaveCriticalSection
 [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
 [WinAPI EndPaint] Handle: -1220611768 PS.HDC: -1220713544

Реализация рисования в событии OnPaint формы или другого элемента управления

Для рисования в событии OnPaint формы само событие приходит из библиотеки лежащих в основе виджетов. Интерфейс LCL должен обработать это событие и создать соответствующий объект DC в обработчике событий, а затем вызвать LCLSendPaintMsg. Кроме того, следует также реализовать соответствующие методы рисования, такие как Rectangle или ExtTextOut. Также могут быть полезны процедуры, связанные с Pen(перо), Brush(кисть) и Font(шрифт).

Вот обработчик события OnPaint из набора виджетов Cocoa, который показывает, как он вызывает LCLSentPaintMsg:

procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext;
  const bounds, dirty:NSRect);
var
  struct : TPaintStruct;
begin
  if not Assigned(Context) then Context:=TCocoaContext.Create;

  Context.ctx:=ControlContext;
  if Context.InitDraw(Round(bounds.size.width), Round(bounds.size.height)) then
  begin
    FillChar(struct, SizeOf(TPaintStruct), 0);
    struct.hdc := HDC(Context);
    {$IFDEF VerboseWinAPI}
      DebugLn(Format('[TLCLCommonCallback.Draw] OnPaint event started context: %x', [HDC(context)]));
    {$ENDIF}
    LCLSendPaintMsg(Target, HDC(Context), @struct);
    {$IFDEF VerboseWinAPI}
      DebugLn('[TLCLCommonCallback.Draw] OnPaint event ended');
    {$ENDIF}
  end;
end;

И список подпрограмм WinAPI, которые были вызваны при запуске этого события:

 [TCocoaWidgetSet.GetDC] hWnd: 0 Result: 12400C0
 [TLCLCommonCallback.Draw] OnPaint event started context: 1240340
 TCocoaWidgetSet.CreatePenIndirect
 TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC460
 TCocoaWidgetSet.SelectObject Result: 0
 TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EBF00
 TCocoaWidgetSet.SelectObject Result: 0
 [TCocoaWidgetSet.Rectangle] DC: 1240340 X1: 100 Y1: 100 X2: 200 Y2: 200
 TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC4A0
 TCocoaWidgetSet.SelectObject Result: 0
 [TCocoaWidgetSet.GetTextExtentPoint] DC: 1240340 Str: Some text Count: 9
 [TCocoaWidgetSet.GetTextExtentPoint] Size: 65,17
 [TLCLCommonCallback.Draw] OnPaint event ended

Внедрение TLabel

Внедрить TLabel особенно сложно, несмотря на то, что он является этаким базовым компонентом, потому что это требует, чтобы была реализована почти вся отрисовка. TLabel не является оконным элементом управления, вместо этого он зависит от сообщений отрисовки, рисующих [компонент] непосредственно на холсте формы.

Прежде чем пытаться заставить работать TLabel, рекомендуется проверить, работают ли функции рисования, такие как Rectangle, внутри события OnPaint формы.

Необходимо реализовать несколько методов WinAPI, в частности:

Методы контекста устройства

BeginPaint, GetDC, EndPaint, ReleaseDC, CreateCompatibleDC

см. Контексты устройств и объекты GDI в интерфейсах LCL

Методы объектов GDI

SelectObject, DeleteObject, CreateFontIndirect, CreateFontIndirectEx

Разные функции

InvalidateRect, GetClientBounds, SetWindowOrgEx

Методы рисования текста

DrawText.
Вместо реализации DrawText можно также использовать стандартный TWidgetSet.DrawText, как это делают наборы виджетов Carbon и Cocoa. Но в этом случае необходимо, чтобы каждый реализовывал хотя бы GetTextMetrics, GetTextExtentPoint и ExtTextOut. Без GetTextMetrics форма с TLabel рухнет, потому что авторазмер не сможет рассчитать подходящий размер для TLabel.

Функции Region для определения, что элемент управления находится за другим

CombineRgn, CreateRectRgn, GetClipRGN, RectVisible


Ниже приведен порядок вызова процедур рисования в форме с одним TLabel, чтобы лучше понять последовательность рисования:

1 - GetDC вызывается один раз при запуске программного обеспечения с hWnd = 0
2 - Показывается форма
3 - GetDC вызывается снова (это не произошло бы без TLabel). Вызывается несколько функций, связанных со шрифтами, а также DrawText с CalcRect, установленным в True, чтобы вычислить размер TLabel.
4 - InvalidateRect вызывается на холсте формы
5 - Управление возвращается к операционной системе до тех пор, пока из набора виджетов не придет сообщение отрисовки
6 - Вызывается BeginPaint, и в этот момент будет выполнен код в событии OnPaint формы
7 - DrawText вызывается снова с CalcRect установленным в false
8 - Отрисовка закончена

Реализация видимости для форм и элементов управления и состояния окна

Код, который управляет видимостью, разделен между видимостью для форм и для элементов управления.

Видимость для форм и состояния окна

Эта часть также управляет состоянием окна (свернуто, развернуто или нормально). Это реализовано как копия функции Windows API ShowWindow, поэтому вы должны реализовать TMyWidgetset.ShowWindow в файле mywinapi.inc. Не забудьте также добавить заголовок в файл mywinapih.inc.

Ниже приведен код, который реализует эту функцию в наборе виджетов Qt. Это должно [помочь] очень легко понять, скопировать и реализовать его на вашем собственном виджете. Вы также можете посмотреть, как это реализует Gtk. В Windows, конечно, API Windows вызывается напрямую, поэтому нет кода, на котором его можно [продемонстрировать].

{------------------------------------------------------------------------------
  function ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;

  nCmdShow:
    SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED
------------------------------------------------------------------------------}
function TQtWidgetSet.ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
var
  Widget: QWidgetH;
begin
  {$ifdef VerboseQtWinAPI}
    WriteLn('WinAPI ShowWindow');
  {$endif}

  Result := False;
  
  Widget := QWidgetH(hWnd);

//  if Widget = nil then RaiseException('TQtWidgetSet.ShowWindow  hWnd is nil');

  case nCmdShow of
    SW_SHOW: QWidget_setVisible(Widget, True);
    SW_SHOWNORMAL: QWidget_showNormal(Widget);
    SW_MINIMIZE: QWidget_setWindowState(Widget, QtWindowMinimized);
    SW_SHOWMINIMIZED: QWidget_showMinimized(Widget);
    SW_SHOWMAXIMIZED: QWidget_showMaximized(Widget);
    SW_HIDE: QWidget_setVisible(Widget, False);
  end;

  Result := True;
end;

Видимость для элементов управления

Для элементов управления внутри формы вам необходимо реализовать функцию класса TMyWSWinControl.ShowHide, которая находится в классе TMyWSWinControl в файле mywscontrols.pp

Помните, что большинство элементов управления наследуются от TWinControl, поэтому реализация этой функции гарантирует, что свойство Visible реализовано для всех стандартных элементов управления, которые его имеют. Ниже приведен пример кода для набора виджетов Qt.

{------------------------------------------------------------------------------
  Method: TQtWSWinControl.ShowHide
  Params:  AWinControl     - the calling object

  Returns: Nothing

  Shows or hides a widget.
 ------------------------------------------------------------------------------}
class procedure TQtWSWinControl.ShowHide(const AWinControl: TWinControl);
begin
  if AWinControl = nil then exit;

  if not AWinControl.HandleAllocated then exit;

  if AWinControl.HandleObjectShouldBeVisible then
   QWidget_setVisible(TQtWidget(AWinControl.Handle).Widget, True)
  else QWidget_setVisible(TQtWidget(AWinControl.Handle).Widget, False);
end;

Реализация компонентов на основе TStrings

Некоторые компоненты используют TStrings для хранения отображаемой информации, например: TCustomMemo, TCustomListBox и TCustomComboBox.

Для их реализации недостаточно просто реализовать их функции, например, в классе TQtCustomMemo. Одна из реализуемых функций будет называться GetStrings и выглядеть следующим образом:

class function TQtWSCustomListBox.GetStrings(const ACustomListBox: TCustomListBox): TStrings;
var
  ListWidgetH: QListWidgetH;
begin
  ListWidgetH := QListWidgetH((TQtWidget(ACustomListBox.Handle).Widget));
  Result := TQtListStrings.Create(ListWidgetH, ACustomListBox);
end;

Эта функция должна возвращать потомок TStrings, предназначенный для отслеживания, что строки добавляются или удаляются из списка, и отправлять эту информацию в набор виджетов для обновления элемента управления. Например, следующее объявление показывает, как выглядит TQtListStrings:

  TQtListStrings = class(TStrings)
  private
    FListChanged: Boolean; // StringList и QtListWidget не синхронизированы
    FStringList: TStringList; // Содержит пункты для показа
    FQtListWidget: QListWidgetH;  // Qt Widget
    FOwner: TWinControl;      // Lazarus'овский владелец элементов управления ListStrings
    FUpdating: Boolean;       // Мы меняем Qt Widget
    procedure InternalUpdate;
    procedure ExternalUpdate(var Astr: TStringList; Clear: Boolean = True);
    procedure IsChanged; // событие OnChange, вызваемое действием программы
  protected
    function GetTextStr: string; override;
    function GetCount: integer; override;
    function Get(Index : Integer) : string; override;
    //procedure SetSorted(Val : boolean); virtual;
  public
    constructor Create(ListWidgetH : QListWidgetH; TheOwner: TWinControl);
    destructor Destroy; override;
    procedure Assign(Source : TPersistent); override;
    procedure Clear; override;
    procedure Delete(Index : integer); override;
    procedure Insert(Index : integer; const S: string); override;
    procedure SetText(TheText: PChar); override;
    //procedure Sort; virtual;
  public
    //property Sorted: boolean read FSorted write SetSorted;
    property Owner: TWinControl read FOwner;
    function ListChangedHandler(Sender: QObjectH; Event: QEventH): Boolean; cdecl;
  end;

Вы можете увидеть его реализацию в модуле qtobjects.pas интерфейса qt.

Реализация меню

Меню доступны на LCL для создания основных меню или всплывающих меню. TMenu является владельцем более крупной структуры меню со многими пунктами. Элементы могут иметь подэлементы, и не нуждаются в дополнительном TMenus.

Также помните, что в LCL дескриптор создается только при необходимости, и в это время все свойства элементов управления уже инициализированы. Это очень помогает для наборов виджетов, где в зависимости от свойств элемента меню он может принадлежать к одному или другому классу, например, Qt.

Следующие вещи должны быть реализованы для того, чтобы меню работали:

1) Все методы модуля QtWSMenus, которые будут реализовывать создание и изменение меню

2) функция TWinCEWidgetSet.SetMenu(AWindowHandle: HWND; AMenuHandle: HMENU): Boolean; из файла wincewinapi.inc, в котором будет реализована поддержка главного меню, связанного с окном.

Порядок создания меню

При реализации меню важно понимать, в каком порядке они создаются. Например, мы хотим создать следующую структуру меню:

Menu creation order.png

И когда наше приложение будет выполнено, при каждом вызове TQtWSMenuItem.CreateHandle будет появляться сообщение 'Creating MenuItem'(Создание MenuItem) с заголовком меню и сообщение 'Creating Menu'(Создание меню) с названием меню (потомки TMenu не имеют подпись), каждый раз, когда вызывается TQtWSMenu.CreateHandle.

Вот результирующий вывод такого программного обеспечения:

 Creating Menu. Name: MainMenu1
 Creating MenuItem: Item1           Parent=Menu.Items : TMenuItem
 Creating MenuItem: SubItem11       Parent=Item1      : TMenuItem
 Creating MenuItem: SubItem12       Parent=Item1      : TMenuItem
 Creating MenuItem: SubItem13       Parent=Item1      : TMenuItem
 Creating MenuItem: SubItem14       Parent=Item1      : TMenuItem
 Creating MenuItem: SubSubItem141   Parent=SubItem14  : TMenuItem
 Creating MenuItem: SubSubItem142   Parent=SubItem14  : TMenuItem
 Creating MenuItem: SubSubItem143   Parent=SubItem14  : TMenuItem
 Creating MenuItem: SubSubItem144   Parent=SubItem14  : TMenuItem
 Creating MenuItem: Item2           Parent=Menu.Items : TMenuItem
 Creating MenuItem: SubItem21       Parent=Item2      : TMenuItem
 Creating MenuItem: SubItem22       Parent=Item2      : TMenuItem
 Creating MenuItem: SubItem23       Parent=Item2      : TMenuItem
 Creating MenuItem: Item3           Parent=Menu.Items : TMenuItem
 Creating MenuItem: Item4           Parent=Menu.Items : TMenuItem

Для всех MenuItems можно использовать GetParentMenu, чтобы получить владельца их родителя: Menu (TMainMenu).

Включение/отключение элемента управления

Текущий способ установить включение/отключение элемента управления - реализация winapi EnableWindow. Этот API должен работать в общем на любом элементе управления. Он должен включать/отключать ввод с помощью мыши и клавиатуры для указанного окна или элемента управления, а также помечать его как недоступный для редактирования пользователем, например, делая его серым.

{------------------------------------------------------------------------------
  Method:  EnableWindow
  Params: HWnd    - handle to window / дескриптор окна
          BEnable -  whether to enable the window / нужно ли включать окно
  Returns: If the window was previously disabled / возращаемый результат: если окно было ранее отключено

  Enables or disables mouse and keyboard input to the specified window or
  control / Включает или отключает ввод с клавиатуры и мыши в указанное окно или
  элемент управления
 ------------------------------------------------------------------------------}
function TWin32WidgetSet.EnableWindow(hWnd: HWND; bEnable: Boolean): Boolean;

Фигурные окна

Окна могут быть сформированы на основе растрового изображения (TBitmap) или региона(TRegion). Регион является видимой частью

Чтобы реализовать фигурные окна на основе TBitmap, реализуйте TWSWinControl.setShape

Для реализации фигурных окон на основе TRegion реализуйте LCLIntf.SetWindowRgn

См.также: Создание не прямоугольных окон или элементов управления

Системные цвета

Некоторые константы цвета на самом деле являются системными цветами, такими как clBtnFace, clForm, clWindow и проч. и проч.

Для реализации поддержки системных цветов должна быть реализована подпрограмма WinAPI GetSysColor:

function GetSysColor(nIndex: Integer): DWORD; override;

А вот фрагмент цветовых констант, которые должны поддерживаться. Проверьте LCLType для последних значений:

//==============================================
// API system Color constants  pbd
// note these are usually shown ORed with
// $80000000 as these would have interfered with
// other MS color enumerations
// GetSysColor and SetSysColor expects the values
// below
//==============================================

type
  COLORREF = LongInt;
  TColorRef = COLORREF;

const
  CLR_INVALID = TColorRef($FFFFFFFF);

  COLOR_SCROLLBAR = 0;
  COLOR_BACKGROUND = 1;
  COLOR_ACTIVECAPTION = 2;
  COLOR_INACTIVECAPTION = 3;
  COLOR_MENU = 4;
  COLOR_WINDOW = 5;
  COLOR_WINDOWFRAME = 6;
  COLOR_MENUTEXT = 7;
  COLOR_WINDOWTEXT = 8;
  COLOR_CAPTIONTEXT = 9;
  COLOR_ACTIVEBORDER = 10;
  COLOR_INACTIVEBORDER = 11;
  COLOR_APPWORKSPACE = 12;
  COLOR_HIGHLIGHT = 13;
  COLOR_HIGHLIGHTTEXT = 14;
  COLOR_BTNFACE = 15;
  COLOR_BTNSHADOW = 16;
  COLOR_GRAYTEXT = 17;
  COLOR_BTNTEXT = 18;
  COLOR_INACTIVECAPTIONTEXT = 19;
  COLOR_BTNHIGHLIGHT = 20;
  COLOR_3DDKSHADOW = 21;
  COLOR_3DLIGHT = 22;
  COLOR_INFOTEXT = 23;
  COLOR_INFOBK = 24;
  // PBD: 25 is unassigned in all the docs I can find
  //      if someone finds what this is supposed to be then fill it in
  //      note defaults below, and cl[ColorConst] in graphics
  COLOR_HOTLIGHT = 26;
  COLOR_GRADIENTACTIVECAPTION = 27;
  COLOR_GRADIENTINACTIVECAPTION = 28;
  COLOR_MENUHILIGHT = 29;
  COLOR_MENUBAR = 30;

  COLOR_FORM = 31;

  COLOR_ENDCOLORS = COLOR_FORM;

  COLOR_DESKTOP = COLOR_BACKGROUND;
  COLOR_3DFACE = COLOR_BTNFACE;
  COLOR_3DSHADOW = COLOR_BTNSHADOW;
  COLOR_3DHIGHLIGHT = COLOR_BTNHIGHLIGHT;
  COLOR_3DHILIGHT = COLOR_BTNHIGHLIGHT;
  COLOR_BTNHILIGHT = COLOR_BTNHIGHLIGHT;

  MAX_SYS_COLORS = COLOR_ENDCOLORS;
  SYS_COLOR_BASE = TColorRef($80000000);

ShowMessage

Эти стандартные диалоги реализованы исключительно в LCL в следующих местах:

  • Файл класса TPromptDialog lcl/include/promptdialog.inc

SpinEdit

И TFloatSpinEdit, и TSpinEdit реализованы в классе TWSFloatSpinEdit.

Буфер обмена

Поддержка буфера обмена реализована в lclintf путем реализации подпрограмм Windows API. Это процедуры:

function ClipboardFormatToMimeType(FormatID: TClipboardFormat): string; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
function ClipboardGetData(ClipboardType: TClipboardType;
  FormatID: TClipboardFormat; Stream: TStream): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
// ! ClipboardGetFormats: List будет создан. Вы должны освободить его самостоятельно с помощью FreeMem(List) !
function ClipboardGetFormats(ClipboardType: TClipboardType;
  var Count: integer; var List: PClipboardFormat): boolean;  {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
function ClipboardGetOwnerShip(ClipboardType: TClipboardType;
  OnRequestProc: TClipboardRequestEvent;  FormatCount: integer;
  Formats: PClipboardFormat): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
function ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}

Последовательность вызовов при запуске программы:

TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/bmp Result=51027040
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/delphi.bitmap Result=0
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/xpm Result=0
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/png Result=0
TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/jpeg Result=0

Последовательность вызовов во время присвоения "Caption:= Clipboard.AsText":

TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=text/plain Result=51026976
TCocoaWidgetSet.ClipboardGetData ClipboardType=clipboard FormatID: 51026976

TSpeedButton

Этот элемент управления является потомком TGraphicControl, поэтому он не имеет дескриптора и рисуется сам. Чтобы сохранить собственный внешний вид, он использует функцию ThemeServices.DrawElement(Details.Element=teButton) для отрисовки самого себя, поэтому реализуйте эту функцию, чтобы этот элемент управления работал. Естественно, многие другие DC, WindowOrg, рисование текста и рисование должны работать, как объяснено для TLabel.

TRadioButton и TCheckButton

Сообщения:

В основном Widgetset должен делать:

  • Отправлять LM_CHANGE, когда радиокнопка не отмечена/отмечена (LCL позаботится о том, будет ли вызываться OnClick или нет)
  • Не отправлять LM_CHANGE, когда вызывается TWSCustomCheckBox.SetState(SetChecked)

См. также этот отчет об ошибке: http://bugs.freepascal.org/view.php?id=13939

Поддержка FullScreen

FullScreen(окно на весь экран без рамки - прим.перев.) реализован как состояние окна wsFullScreen и, как таковой, реализован в LCLIntf.ShowWindow, где должна обрабатываться константа SW_SHOWFULLSCREEN.

Пример работы интерфейсов LCL

Ниже приведен простой пример. Предположим, вы разработали компонент trayicon. Как бы вы реализовали его в LCL для правильной работы на различных поддерживаемых платформах?

Вам нужно будет сгенерировать следующие файлы:

\trayicon.pas
\wstrayicon.pas
\gtk\gtkwstrayicon.pas
\gtk\trayintf.pas
\win32\win32wstrayicon.pas
\win32\trayintf.pas

Предоставление отдельных файлов wsXXX.pas и \$(LCLWidgetType)\XXXintf.pas полностью исключает необходимость использования ifdefs. Вам нужно будет добавить в качестве пути модуля $(LCLWidgetType) в файл XXXintf.pas, чтобы инициализировать корректный файл trayintf.pas, который, в свою очередь, инициализирует корректный класс WS Tray.

Внутри trayicon.pas вы включаете wstrayicon. Извлеките свой основной класс из класса LCL и используйте только wstrayicon в его реализации. Все классы LCL, которые взаимодействуют с набором виджетов, должны быть получены из TLCLComponent, объявленного в модуле LCLClasses.

unit TrayIcon;

interface

type
  TTrayIcon = class(TLCLComponent)
  public
    procedure DoTray;
  end;

implementation

uses wstrayicon;

procedure TTrayIcon.DoTray;
begin
  // вызов wstrayicon
end;

end.

в trayintf вы используете gtkwstrayicon или win32trayicon, в зависимости от того, какой это файл trayintf.

в wstrayicon вы создаете класс следующим образом:

unit WSTrayIcon;

uses WSLCLClasses, Controls, TrayIcon; // и другие модули

TWSTrayIconClass = class of TWSTrayIcon;
TWSTrayIcon = class(TWSWinControl);
public
 // все они должны быть виртуальными и классовыми процедурами !!
 class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual; 
 class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;
 ....
end;
...

implementation

procedure TWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
begin
 //ничего не делаем
end;

procedure TWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
begin
 //ничего не делаем
end;

теперь в gtkwstrayicon.pas сделайте так:

uses WSTrayIcon, WSLCLClasses, Controls, TrayIcon, gtk, gdk;


TGtkWSTrayIcon = class(TWSTrayIcon);
private
 class function FindSystemTray(const ATrayIcon: TCustomTrayIcon): TWindow; virtual;public
 class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
 class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
 class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override;
 ....
end;
...

implementation

procedure TGtkWSTrayIcon.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
var
WidgetInfo: PWidgetInfo;
begin
 Result := gtk_plug_new;
 WidgetInfo := CreateWidgetInfo(AWinControl, Result); // как-то так или иначе
 TGtkWSWincontrolClass(WidgetSetClass).SetCallbacks(AWinControl);
 // и многое другое
end;

function TGtkWSTrayIcon.FindSystemTray(const ATrayIcon:
TCustomTrayIcon): TWindow;
begin
 // что-то делаем
end;


procedure TGtkWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
var
SystemTray: TWindow;
begin
 SystemTray := FindSystemTray(ATrayIcon);
 //что-то делаем
end;

procedure TGtkWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
begin
 //что-то делаем
end;

......

initialization

RegisterWSComponent(TCustomTrayIcon, TGtkWSTrayIcon); //вот это очень важно!!!

end.

затем наконец-то в trayicon.pas вы пишете как обычно

uses WSTrayIcon; //и т.д., вы НЕ включаете сюда GtkWSTrayIcon!

TCustomTrayIcon = class(TWinControl)
public
 procedure EmbedControl;
....
end;

...
procedure TTrayIcon.EmbedControl;
begin
 TWSTrayIconClass(WidgetSetClass).EmbedControl(Self);
end;

Этот документ находится в стадии разработки. Вы можете помочь, написав разделы этого документа. Если вы не можете найти информацию, которую вы ищете в этом документе, пожалуйста, добавьте свой вопрос на страницу обсуждения. Это поможет нам писать документацию на уровне, который не является ни слишком простым, ни слишком сложным, и полностью охватывает области, о которых люди хотят знать.