Sign in Join
Wiki copytemporary

LCL Internals/ru: what changed

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

OldNewLine
11## Другие интерфейсы
22
33- [[Lazarus known issues (things that will never be fixed)]] - Список проблем совместимости интерфейса
44- [[Win32/64 Interface]] - Интерфейс winapi для Windows 95/98/Me/2K/XP/Vista, но не для CE
55- [[Windows CE Interface]] - Для карманных ПК и смартфонов
66- [[Carbon Interface]] - Интерфейс Carbon для macOS
77- [[Cocoa Interface]] - Интерфейс Cocoa для macOS
88- [[Qt Interface]] - Интерфейс Qt4 для Unix, macOS, Windows и КПК на базе Linux
99- [[Qt5 Interface]] - Интерфейс Qt5 для Unix, macOS, Windows и КПК на базе Linux
1010- [[GTK1 Interface]] - Интерфейс gtk1 для Unix, macOS (X11), Windows
1111- [[GTK2 Interface]] - Интерфейс gtk2 для Unix, macOS (X11), Windows
1212- [[GTK3 Interface]] - Интерфейс gtk3 для Unix, macOS (X11), Windows
1313- [[fpGUI Interface]] - Основан на библиотеке fpGUI, которая представляет собой кроссплатформенный инструментарий, полностью написанный на Object Pascal
1414- [[Custom Drawn Interface]] - Кроссплатформенный бэкэнд LCL, написанный полностью на Object Pascal внутри Lazarus. Интерфейс Lazarus для Android.
1515
1616### Советы для конкретных платформ
1717
1818- [[Windows Programming Tips]] - Cоветы по программированию на desktop-Windows.
1919- [[Linux Programming Tips]] - Как выполнять определенные задачи программирования в Linux
2020- [[macOS Programming Tips]] - Советы по Lazarus, полезные инструменты, команды Unix и многое другое ...
2121- [[WinCE Programming Tips]] - Использование телефонного API, отправка SMS-сообщений и многое другое ...
2222- [[Android Programming]] - Для Android смартфонов и планшетов
2323- [[iPhone/iPod development]] - Об использовании Objective Pascal для разработки приложений для iOS
2424
2525### Статьи по разработке интерфейсов
2626
2727- [[Carbon interface internals]] - Если вы хотите помочь улучшить интерфейс Carbon
2828- [[Windows CE Development Notes]] - Для карманных ПК и смартфонов
2929- [[Adding a new interface]] - Как добавить новый интерфейс набора виджетов
3030- [[LCL Defines]] - Выбор правильных опций для перекомпиляции LCL
3131- [[LCL Internals]] - Некоторая информация о внутренней работе LCL
3232- [[Cocoa Internals]] - Некоторая информация о внутренней работе виджета Cocoa
3333
3434## Минимальные версии Toolkit
3535
3636| Lazarus version | Мин. FPC | Мин. Gtk 2 | Мин. Qt 4 | Мин. Windows | Мин. Windows CE | Мин. macOS (Carbon) | Мин. macOS (Cocoa) | Мин. треб. LCL-CustomDrawn |
3737|----|----|----|----|----|----|----|----|----|
3838| 0.9.24 | 2.2.0 | 2.6+ | 4.2+ | Windows 98+ | Рекомендуется 4.0+ | 10.4 | N/A | N/A |
3939| 0.9.26 | 2.2.2 | 2.6+ | 4.3+ | Windows 98+ | Рекомендуется 4.0+ | 10.4 | N/A | N/A |
4040| 0.9.28 | 2.2.4 | 2.8+ | 4.5+ | Windows 98+ | Рекомендуется 4.0+ | 10.4 | N/A | N/A |
4141| 0.9.30 | 2.4.0 | 2.8+ | 4.5+ | Windows 98+ | Рекомендуется 5.0+ | 10.4 | N/A | N/A |
4242| 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+ |
4343| 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+ |
4444
4545- \- На самом деле 4.5.0 ... 4.8.4 с предварительно собранным Qt4Pas.dll, 4.8.5+ требует другого Qt4Pas.dll
4646
4747См.также: [[Installing Lazarus on MacOS X/ru|совместимость]]
4848
4949## Внутренности LCL
5050
5151Есть LCL и есть «интерфейс». LCL - это часть, которая не зависит от платформы и находится в каталоге lazarus/lcl/. Этот каталог содержит в основном определения классов. Многие различные элементы управления фактически реализованы в каталоге lazarus/lcl/include/ в различных файлах .inc. Это делается для того, чтобы быстрее найти реализацию определенного элемента управления, например TCustomMemo (который находится в custommemo.inc). Каждый .inc начинается со строки {% MainUnit ...}, чтобы определить, где он включен.
5252
5353Также есть «интерфейс», который находится в подкаталоге каталога 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.
5454
5555Каждый элемент управления имеет свойство WidgetSetClass, принадлежащее "зеркальному" классу в каталоге интерфейсов, например: "зеркало" для TCustomEdit - это TWSCustomEdit, методы которого реализуются TWin32WSCustomEdit в win32wsstdctrls. Это способ, которым LCL взаимодействует с интерфейсом, и то, как он позволяет интерфейсу делать свое дело.
5656
5757Передача интерфейса обратно в LCL в основном осуществляется путем отправки сообщений, обычно 'DeliverMessage', который вызывает TControl.Perform (\<message_id\>, wparam, lparam), где wparam и lparam являются дополнительной информацией для сообщения.
5858
5959### Pie и RadialPie
6060
6161Модуль LCLIntf содержит две функции для рисования округлых фигур:
6262
63-`function `[[doc:lcl/lclintf/pie.html|Pie]]`(DC: HDC; x1, y1, x2, y2, sx, sy, ex, ey: Integer): Boolean;`\
64-`function `[[doc:lcl/lclintf/radialpie.html|RadialPie]]`(DC: HDC; x1, y1, x2, y2, Angle1, Angle2: Integer): Boolean; `
63+``` pascal
64+function Pie(DC: HDC; x1, y1, x2, y2, sx, sy, ex, ey: Integer): Boolean;
65+function RadialPie(DC: HDC; x1, y1, x2, y2, Angle1, Angle2: Integer): Boolean;
66+```
6567
6668Функция Pie использует две точки (sx,sy) и (ex,ey) для указания начала и конца дуги. RadialPie использует углы, чтобы указать начало и конец дуги.
6769
6870Pie вызывает TWidgetSet.Pie, а RadialPie вызывает TWidgetSet.RadialPie. Реализация TWidgetSet.Pie по умолчанию заключается в преобразовании параметров в углы и вызове TWidgetSet.RadialPie. TWidgetSet.RadialPie создает массив точек для дуги и вызывает TWidgetSet.Polygon.
6971
7072Набор виджетов win32 переопределяет TWidgetSet.Pie для непосредственного вызова функции Windows Pie.
7173
7274> **Note:** в более старых версиях Lazarus существовал RadialPie с углами, которые делали то же, что и нынешние RadialPie и RadialPie, которые делают то же самое, что и наша \[функция\] Pie. Эти функции были удалены в Lazarus 0.9.21.
7375
7476### Интерфейсы
7577
7678См. [[LCL Internals/ru|другие интерфейсы]]
7779
7880### Добавление нового модуля в LCL
7981
8082Сначала добавьте новое имя модуля в allclunits.pp.
8183
8284Чтобы убедиться, что модуль зарегистрирован в палитре компонентов, смотрите файлы RegisterLCL.pas и pkgfileslcl.inc, они расположены в lazarus/packager.
8385
8486## Как создать новый Widgetset
8587
8688Это пошаговое руководство по разработке нового [[Widgetset|widgetset]]. Он основан на моем опыте создания основ нового интерфейса qt4.
8789
8890Для начала, почему кто-то хочет добавить Widgetset? Ответ заключается в том, чтобы иметь возможность портировать существующее программное обеспечение Lazarus на большее количество платформ без изменения их кода.
8991
9092Итак, давайте напишем набор виджетов. Прежде всего, вам нужно иметь паскаль привязки для виджета и знать, как его использовать. Обычно это не сложно. Несколько часов работы с основными учебными пособиями, доступными в Интернете, должны быть достаточными для начала. Если привязки еще не существуют, их нужно создать. Если учебные пособия написаны на другом языке, переведите их на паскаль и заставьте их работать.
9193
9294Теперь для Qt я использовал привязки Den Jean qt4 для паскаля и создал очень простую программу Qt, используя их:
9395
9496``` pascal
9597program qttest;
9698
9799uses qt4;
98100
99101var
100102 App: QApplicationH;
101103 MainWindow: QMainWindowH;
102104begin
103105 App := QApplication_Create(@argc,argv);
104106
105107 MainWindow := QMainWindow_Create;
106108
107109 QWidget_show(MainWindow);
108110
109111 QApplication_Exec;
110112end.
111113```
112114
113115Вышеуказанный проект компилируется и создает программу qt4. Теперь мы будем использовать его код для написания нового набора виджетов. После того, как мы закончим, приведенная ниже программа lazarus прекрасно скомпилируется в программу qt4:
114116
115117``` pascal
116118program qttest;
117119
118120{$mode objfpc}{$H+}
119121
120122uses
121123 Interfaces, Classes, Forms,
122124 { Add your units here }
123125 qtform;
124126
125127begin
126128 Application.Initialize;
127129 Application.CreateForm(TForm1, Form1);
128130 Application.Run;
129131end.
130132```
131133
132134где форма поддерживается Lazarus IDE и разрабатывается визуально.
133135
134136Первое, что нужно сделать с новым набором виджетов, это добавить пустой скелет для него. Очень ранние наборы виджетов разработки, такие как qt и carbon, могут служить скелетом.
135137
136138Просматривая файлы на многих виджетах, вы можете увидеть первый файл, который будет вызван lcl: Interfaces.pas. Этот файл просто вызывает другой файл с именем QtInt.pas или аналогичный. QtInt.pas содержит код для класса TWidgetSet, который мы должны реализовать. На пустом скелете вы можете видеть, что у класса есть различные функции, которые он должен реализовать:
137139
138140``` pascal
139141 TQtWidgetSet = Class(TWidgetSet)
140142 private
141143 App: QApplicationH;
142144 public
143145 {$I qtwinapih.inc}
144146 {$I qtlclintfh.inc}
145147 public
146148 // Application
147149 procedure AppInit(var ScreenInfo: TScreenInfo); override;
148150 procedure AppRun(const ALoop: TApplicationMainLoop); override;
149151 procedure AppWaitMessage; override;
150152 procedure AppProcessMessages; override;
151153 procedure AppTerminate; override;
152154 procedure AppMinimize; override;
153155 procedure AppBringToFront; override;
154156 public
155157 constructor Create;
156158 destructor Destroy; override;
157159 function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override;
158160 procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override;
159161 procedure DCRedraw(CanvasHandle: HDC); override;
160162 procedure SetDesigning(AComponent: TComponent); override;
161163
162164 function InitHintFont(HintFont: TObject): Boolean; override;
163165
164166 // create and destroy
165167 function CreateComponent(Sender : TObject): THandle; override; // deprecated
166168 function CreateTimer(Interval: integer; TimerFunc: TFNTimerProc): integer; override;
167169 function DestroyTimer(TimerHandle: integer): boolean; override;
168170 end;
169171```
170172
171173### Как реализовать новый оконный компонент
172174
173175Все оконные компоненты являются потомками TWinControl. Эти элементы управления имеют дескриптор и поэтому должны создаваться набором виджетов. Добавлять новые оконные компоненты в набор виджетов \[довольно\] легко.
174176
175177Допустим, вы хотите добавить TQtWSCustomEdit в Qt Widgetset. \[Нужно\] начать с TCustomEdit, \[который\] является потомком TWinControl и находится в модуле StdCtrls.
176178
177179Теперь перейдите к модулю QtWSStrCtrls и найдите объявление TQtWSCustomEdit.
178180
179181``` pascal
180182 TQtWSCustomEdit = class(TWSCustomEdit)
181183 private
182184 protected
183185 public
184186 end;
185187```
186188
187189Добавьте статические методы, объявленные в TWSCustomEdit, и переопределите их. Код должен теперь выглядеть так:
188190
189191``` pascal
190192 TQtWSCustomEdit = class(TWSCustomEdit)
191193 private
192194 protected
193195 public
194196 class function CreateHandle(const AWinControl: TWinControl;
195197 const AParams: TCreateParams): HWND; override;
196198 class procedure DestroyHandle(const AWinControl: TWinControl); override;
197199{ class function GetSelStart(const ACustomEdit: TCustomEdit): integer; override;
198200 class function GetSelLength(const ACustomEdit: TCustomEdit): integer; override;
199201
200202 class procedure SetCharCase(const ACustomEdit: TCustomEdit; NewCase: TEditCharCase); override;
201203 class procedure SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); override;
202204 class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
203205 class procedure SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char); override;
204206 class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
205207 class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
206208 class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
207209
208210 class procedure GetPreferredSize(const AWinControl: TWinControl;
209211 var PreferredWidth, PreferredHeight: integer); override;}
210212 end;
211213```
212214
213215Закомментированная часть кода - это процедуры, которые необходимо реализовать, чтобы TCustomEdit был полностью функциональным, но достаточно просто \[наличия\] CreateHandle и DestroyHandle, чтобы он отображался в форме и был редактируемым, поэтому он соответствует нашим потребностям в этой статье.
214216
215217Нажмите `Ctrl`+`Shift`+`C`, чтобы завершить код и реализовать CreateHandle и DestroyHandle. В случае Qt4 код будет таким:
216218
217219``` pascal
218220{ TQtWSCustomEdit }
219221
220222class function TQtWSCustomEdit.CreateHandle(const AWinControl: TWinControl;
221223 const AParams: TCreateParams): HWND;
222224var
223225 Widget: QWidgetH;
224226 Str: WideString;
225227begin
226228 // Создаем виджет
227229 WriteLn('Calling QTextDocument_create');
228230 Str := WideString((AWinControl as TCustomMemo).Lines.Text);
229231 Widget := QTextEdit_create(@Str, QWidgetH(AWinControl.Parent.Handle));
230232
231233 //Задаем его начальные свойства
232234 QWidget_setGeometry(Widget, AWinControl.Left, AWinControl.Top,
233235 AWinControl.Width, AWinControl.Height);
234236
235237 QWidget_show(Widget);
236238
237239 Result := THandle(Widget);
238240end;
239241
240242class procedure TQtWSCustomEdit.DestroyHandle(const AWinControl: TWinControl);
241243begin
242244 QTextEdit_destroy(QTextEditH(AWinControl.Handle));
243245end;
244246```
245247
246248Теперь раскомментируйте строку "RegisterWSComponent (TCustomEdit, TQtWSCustomEdit);" в конце модуля и ... все!
247249
248250Теперь вы можете поместить TCustomEdit внизу формы и ожидать, что он будет работать. :^)
249251
250252### Реализация TBitmap
251253
252254Для реализации TBitmap необходимо понимать TRawImage и TLazIntfImage, как описано здесь: [[Developing with Graphics|Developing with Graphics#Working with TLazIntfImage.2C TRawImage and TLazCanvas]]
253255
254256Итак, допустим, вы хотите скомпилировать следующий код:
255257
256258``` pascal
257259procedure TMyForm.HandleOnPaint(Sender: TObject);
258260var
259261 Bitmap: TBitmap;
260262begin
261263 Bitmap := TBitmap.Create;
262264 try
263265 Bitmap.LoadFromFile('myfile.bmp');
264266 Canvas.Draw(0, 0, Bitmap);
265267 finally
266268 Bitmap.Free;
267269 end;
268270end;
269271```
270272
271273Ниже приведен порядок вызова функций из интерфейса widgetset при выполнении этого кода:
272274
273-` 1 - BeginPaint`
275+```text
276+ 1 - BeginPaint
277+```
274278
275279Это будет вызвано только, если событие OnPaint отправляет ноль в качестве DC для события рисования
276280
277-` 2 - GetDC(0);`
281+```text
282+ 2 - GetDC(0);
283+```
278284
279285Просто создает контекст устройства.
280286
281-` 3 - TCDWidgetSet.RawImage_QueryDescription `
287+```text
288+ 3 - TCDWidgetSet.RawImage_QueryDescription
289+```
282290
283291Реализация этой процедуры по умолчанию подходит для большинства наборов виджетов.
284292
285-` 4 - TCDWidgetSet.RawImage_CreateBitmaps`
293+```text
294+ 4 - TCDWidgetSet.RawImage_CreateBitmaps
295+```
286296
287297Здесь вам нужно создать собственный объект изображения и загрузить его из RawData.Data, где хранится информация на основе вашего описания формата пикселя в пункте 2.
288298
289-` 5 - CreateCompatibleDC(0)`
299+```text
300+ 5 - CreateCompatibleDC(0)
301+```
290302
291303Это создает временный DC только для хранения изображения, но на данный момент нет информации об изображении, поэтому в этот момент этот DC действительно фиктивный
292304
293-` 6 - SelectObject`
305+```text
306+ 6 - SelectObject
307+```
294308
295309С изображением в качестве объекта, который будет выбран, и DC, созданный выше, в качестве целевого DC.
296310
297-` 7 - StretchMaskBlt`
311+```text
312+ 7 - StretchMaskBlt
313+```
298314
299315Наконец, функция рисования! DestDC - это DC, размещенный на BeginPaint.
300316
301-` 8 - EndPaint`
317+```text
318+ 8 - EndPaint
319+```
302320
303321Опять же, не всегда используется.
304322
305323### TBitmap.LoadFromDevice для снятия скриншота
306324
307325Рекомендуется сначала реализовать Bitmap, прежде чем делать этот шаг.
308326
309327В LCL вы можете использовать следующий код, делающий скриншот со всего экрана и рисующий его на холсте:
310328
311329``` pascal
312330var
313331 ScreenDC: HDC;
314332 Bitmap: TBitmap;
315333begin
316334 Bitmap := TBitmap.Create;
317335 try
318336 ScreenDC := GetDC(0);
319337 Bitmap.LoadFromDevice(ScreenDC);
320338 ReleaseDC(0, ScreenDC);
321339 Canvas.Draw(0, 0, Bitmap);
322340 finally
323341 Bitmap.Free;
324342 end;
325343end;
326344```
327345
328346Если вы уже внедрили TBitmap, для LoadFromDevice нужно реализовать только 2 новые функции: GetDeviceSize и GetRawImageFromDevice
329347
330348Ниже приведен большой трейс, охватывающий все функции набора виджетов, вызываемые для события OnPaint, которое делает снимок экрана и рисует его на экране. Эта трассировка была сделана с помощью набора виджетов Qt и может иметь некоторые недостатки. Номера дескрипторов должны использоваться для проверки того, какой объект используется в функциях.
331349
332350 [WinAPI BeginPaint] Result=-1220713544
333351 [WinAPI GetClientBounds]
334352 [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
335353
336354``` pascal
337355//Входим в событие OnPaint
338356Bitmap := TBitmap.Create;
339357try
340358 ScreenDC := GetDC(0);
341359```
342360
343361 [WinAPI GetDC] hWnd: 0 Result: -1220712920
344362
345363``` pascal
346364Bitmap.LoadFromDevice(ScreenDC);
347365```
348366
349-` [WinAPI GetDeviceSize]`\
350-` [WinAPI GetRawImageFromDevice] SrcDC: -1220712920 SrcWidth: 0 SrcHeight: 0`\
351-` [WinAPI CreateBitmapFromRawImage] Width:1024 Height:768 DataSize: 3145728 CreateMask: False Bitmap:-1220746696`\
352-` [WinAPI GetObject] GDIObj: -1220746696 Result=84 ObjectType=Image`
367+```text
368+ [WinAPI GetDeviceSize]
369+ [WinAPI GetRawImageFromDevice] SrcDC: -1220712920 SrcWidth: 0 SrcHeight: 0
370+ [WinAPI CreateBitmapFromRawImage] Width:1024 Height:768 DataSize: 3145728 CreateMask: False Bitmap:-1220746696
371+ [WinAPI GetObject] GDIObj: -1220746696 Result=84 ObjectType=Image
372+```
353373
354374``` pascal
355375 ReleaseDC(0, ScreenDC);
356376```
357377
358378 [WinAPI ReleaseDC] hWnd: 0 DC: -1220712920
359379
360380``` pascal
361381Canvas.Draw(0, 0, Bitmap);
362382```
363383
364-` [WinAPI CreateCompatibleDC] DC: 0`\
365-` [WinAPI GetDC] hWnd: 0 Result: -1220712920`\
366-` [WinAPI SelectObject] DC=-1220712920 GDIObj=-1220746696 Result=0 ObjectType=Image`\
367-` [WinAPI StretchMaskBlt] DestDC:-1220713544 SrcDC:-1220712920 Image:137185120 X:0 Y:0 W:1024 H:768 XSrc:0 YSrc:0 WSrc:1024 HSrc:768`
384+```text
385+ [WinAPI CreateCompatibleDC] DC: 0
386+ [WinAPI GetDC] hWnd: 0 Result: -1220712920
387+ [WinAPI SelectObject] DC=-1220712920 GDIObj=-1220746696 Result=0 ObjectType=Image
388+ [WinAPI StretchMaskBlt] DestDC:-1220713544 SrcDC:-1220712920 Image:137185120 X:0 Y:0 W:1024 H:768 XSrc:0 YSrc:0 WSrc:1024 HSrc:768
389+```
368390
369391``` pascal
370392finally
371393 Bitmap.Free;
372394end;
373395```
374396
375-` [WinAPI SelectObject] DC=-1220712920 GDIObj=0 Invalid GDI Object`\
376-` [WinAPI DeleteObject] GDIObject: -1220746696 Result=False ObjectType=Image`
397+``` pascal
398+ [WinAPI SelectObject] DC=-1220712920 GDIObj=0 Invalid GDI Object
399+ [WinAPI DeleteObject] GDIObject: -1220746696 Result=False ObjectType=Image
400+```
377401
378402``` pascal
379403//Здесь мы вышли из события OnPaint
380404```
381405
382-` [WinAPI DeleteObject] GDIObject: 0`\
383-` [WinAPI DeleteObject] GDIObject: 0`\
384-` [WinAPI DeleteObject] GDIObject: 0`\
385-` [WinAPI DeleteObject] GDIObject: 0`\
386-` [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: -152 NewY: -246`\
387-` [WinAPI DeleteObject] GDIObject: 0`\
388-` [WinAPI DeleteObject] GDIObject: 0`\
389-` [WinAPI DeleteObject] GDIObject: 0`\
390-` [WinAPI DeleteObject] GDIObject: 0`\
391-` TWidgetSet.InitializeCriticalSection`\
392-` TWidgetSet.EnterCriticalSection`\
393-` [WinAPI SelectObject] DC=-1220713544 GDIObj=0 Invalid GDI Object`\
394-` [WinAPI MoveToEx] DC:-1220713544 X:0 Y:0`\
395-` [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746760 Result=-1220746856 ObjectType=Brush`\
396-` [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746856 Result=-1220746856 ObjectType=Brush`\
397-` TWidgetSet.LeaveCriticalSection`\
398-` [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0`\
399-` [WinAPI EndPaint] Handle: -1220611768 PS.HDC: -1220713544`
406+``` pascal
407+ [WinAPI DeleteObject] GDIObject: 0
408+ [WinAPI DeleteObject] GDIObject: 0
409+ [WinAPI DeleteObject] GDIObject: 0
410+ [WinAPI DeleteObject] GDIObject: 0
411+ [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: -152 NewY: -246
412+ [WinAPI DeleteObject] GDIObject: 0
413+ [WinAPI DeleteObject] GDIObject: 0
414+ [WinAPI DeleteObject] GDIObject: 0
415+ [WinAPI DeleteObject] GDIObject: 0
416+ TWidgetSet.InitializeCriticalSection
417+ TWidgetSet.EnterCriticalSection
418+ [WinAPI SelectObject] DC=-1220713544 GDIObj=0 Invalid GDI Object
419+ [WinAPI MoveToEx] DC:-1220713544 X:0 Y:0
420+ [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746760 Result=-1220746856 ObjectType=Brush
421+ [WinAPI SelectObject] DC=-1220713544 GDIObj=-1220746856 Result=-1220746856 ObjectType=Brush
422+ TWidgetSet.LeaveCriticalSection
423+ [WinAPI SetWindowOrgEx] DC: -1220713544 NewX: 0 NewY: 0
424+ [WinAPI EndPaint] Handle: -1220611768 PS.HDC: -1220713544
425+```
400426
401427### Реализация рисования в событии OnPaint формы или другого элемента управления
402428
403429Для рисования в событии OnPaint формы само событие приходит из библиотеки лежащих в основе виджетов. Интерфейс LCL должен обработать это событие и создать соответствующий объект DC в обработчике событий, а затем вызвать LCLSendPaintMsg. Кроме того, следует также реализовать соответствующие методы рисования, такие как Rectangle или ExtTextOut. Также могут быть полезны процедуры, связанные с Pen(перо), Brush(кисть) и Font(шрифт).
404430
405431Вот обработчик события OnPaint из набора виджетов Cocoa, который показывает, как он вызывает LCLSentPaintMsg:
406432
407433``` pascal
408434procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext;
409435 const bounds, dirty:NSRect);
410436var
411437 struct : TPaintStruct;
412438begin
413439 if not Assigned(Context) then Context:=TCocoaContext.Create;
414440
415441 Context.ctx:=ControlContext;
416442 if Context.InitDraw(Round(bounds.size.width), Round(bounds.size.height)) then
417443 begin
418444 FillChar(struct, SizeOf(TPaintStruct), 0);
419445 struct.hdc := HDC(Context);
420446 {$IFDEF VerboseWinAPI}
421447 DebugLn(Format('[TLCLCommonCallback.Draw] OnPaint event started context: %x', [HDC(context)]));
422448 {$ENDIF}
423449 LCLSendPaintMsg(Target, HDC(Context), @struct);
424450 {$IFDEF VerboseWinAPI}
425451 DebugLn('[TLCLCommonCallback.Draw] OnPaint event ended');
426452 {$ENDIF}
427453 end;
428454end;
429455```
430456
431457И список подпрограмм WinAPI, которые были вызваны при запуске этого события:
432458
433-` [TCocoaWidgetSet.GetDC] hWnd: 0 Result: 12400C0`\
434-` [TLCLCommonCallback.Draw] OnPaint event started context: 1240340`\
435-` TCocoaWidgetSet.CreatePenIndirect`\
436-` TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC460`\
437-` TCocoaWidgetSet.SelectObject Result: 0`\
438-` TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EBF00`\
439-` TCocoaWidgetSet.SelectObject Result: 0`\
440-` [TCocoaWidgetSet.Rectangle] DC: 1240340 X1: 100 Y1: 100 X2: 200 Y2: 200`\
441-` TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC4A0`\
442-` TCocoaWidgetSet.SelectObject Result: 0`\
443-` [TCocoaWidgetSet.GetTextExtentPoint] DC: 1240340 Str: Some text Count: 9`\
444-` [TCocoaWidgetSet.GetTextExtentPoint] Size: 65,17`\
445-` [TLCLCommonCallback.Draw] OnPaint event ended`
459+```text
460+ [TCocoaWidgetSet.GetDC] hWnd: 0 Result: 12400C0
461+ [TLCLCommonCallback.Draw] OnPaint event started context: 1240340
462+ TCocoaWidgetSet.CreatePenIndirect
463+ TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC460
464+ TCocoaWidgetSet.SelectObject Result: 0
465+ TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EBF00
466+ TCocoaWidgetSet.SelectObject Result: 0
467+ [TCocoaWidgetSet.Rectangle] DC: 1240340 X1: 100 Y1: 100 X2: 200 Y2: 200
468+ TCocoaWidgetSet.SelectObject DC: 1240340 GDIObj: 10EC4A0
469+ TCocoaWidgetSet.SelectObject Result: 0
470+ [TCocoaWidgetSet.GetTextExtentPoint] DC: 1240340 Str: Some text Count: 9
471+ [TCocoaWidgetSet.GetTextExtentPoint] Size: 65,17
472+ [TLCLCommonCallback.Draw] OnPaint event ended
473+```
446474
447475### Внедрение TLabel
448476
449477Внедрить TLabel особенно сложно, несмотря на то, что он является этаким базовым компонентом, потому что это требует, чтобы была реализована почти вся отрисовка. TLabel не является оконным элементом управления, вместо этого он зависит от сообщений отрисовки, рисующих \[компонент\] непосредственно на холсте формы.
450478
451479Прежде чем пытаться заставить работать TLabel, рекомендуется проверить, работают ли функции рисования, такие как Rectangle, внутри события OnPaint формы.
452480
453481Необходимо реализовать несколько методов WinAPI, в частности:
454482
455483**Методы контекста устройства**
456484
457485BeginPaint, GetDC, EndPaint, ReleaseDC, CreateCompatibleDC
458486
459487см. [[Device Contexts and GDI objects in the LCL interfaces|Контексты устройств и объекты GDI в интерфейсах LCL]]
460488
461489**Методы объектов GDI**
462490
463491SelectObject, DeleteObject, CreateFontIndirect, CreateFontIndirectEx
464492
465493**Разные функции**
466494
467495InvalidateRect, GetClientBounds, SetWindowOrgEx
468496
469497**Методы рисования текста**
470498
471499DrawText.Вместо реализации DrawText можно также использовать стандартный TWidgetSet.DrawText, как это делают наборы виджетов Carbon и Cocoa. Но в этом случае необходимо, чтобы каждый реализовывал хотя бы GetTextMetrics, GetTextExtentPoint и ExtTextOut. Без GetTextMetrics форма с TLabel рухнет, потому что авторазмер не сможет рассчитать подходящий размер для TLabel.
472500
473501**Функции Region для определения, что элемент управления находится за другим**
474502
475503CombineRgn, CreateRectRgn, GetClipRGN, RectVisible
476504
477505Ниже приведен порядок вызова процедур рисования в форме с одним TLabel, чтобы лучше понять последовательность рисования:
478506
479507 1 - GetDC вызывается один раз при запуске программного обеспечения с hWnd = 0
480508 2 - Показывается форма
481509 3 - GetDC вызывается снова (это не произошло бы без TLabel). Вызывается несколько функций, связанных со шрифтами, а также DrawText с CalcRect, установленным в True, чтобы вычислить размер TLabel.
482510 4 - InvalidateRect вызывается на холсте формы
483511 5 - Управление возвращается к операционной системе до тех пор, пока из набора виджетов не придет сообщение отрисовки
484512 6 - Вызывается BeginPaint, и в этот момент будет выполнен код в событии OnPaint формы
485513 7 - DrawText вызывается снова с CalcRect установленным в false
486514 8 - Отрисовка закончена
487515
488516### Реализация видимости для форм и элементов управления и состояния окна
489517
490518Код, который управляет видимостью, разделен между видимостью для форм и для элементов управления.
491519
492520#### Видимость для форм и состояния окна
493521
494522Эта часть также управляет состоянием окна (свернуто, развернуто или нормально). Это реализовано как копия функции Windows API ShowWindow, поэтому вы должны реализовать TMyWidgetset.ShowWindow в файле mywinapi.inc. Не забудьте также добавить заголовок в файл mywinapih.inc.
495523
496524Ниже приведен код, который реализует эту функцию в наборе виджетов Qt. Это должно \[помочь\] очень легко понять, скопировать и реализовать его на вашем собственном виджете. Вы также можете посмотреть, как это реализует Gtk. В Windows, конечно, API Windows вызывается напрямую, поэтому нет кода, на котором его можно \[продемонстрировать\].
497525
498526``` pascal
499527{------------------------------------------------------------------------------
500528 function ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
501529
502530 nCmdShow:
503531 SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED
504532------------------------------------------------------------------------------}
505533function TQtWidgetSet.ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
506534var
507535 Widget: QWidgetH;
508536begin
509537 {$ifdef VerboseQtWinAPI}
510538 WriteLn('WinAPI ShowWindow');
511539 {$endif}
512540
513541 Result := False;
514542
515543 Widget := QWidgetH(hWnd);
516544
517545// if Widget = nil then RaiseException('TQtWidgetSet.ShowWindow hWnd is nil');
518546
519547 case nCmdShow of
520548 SW_SHOW: QWidget_setVisible(Widget, True);
521549 SW_SHOWNORMAL: QWidget_showNormal(Widget);
522550 SW_MINIMIZE: QWidget_setWindowState(Widget, QtWindowMinimized);
523551 SW_SHOWMINIMIZED: QWidget_showMinimized(Widget);
524552 SW_SHOWMAXIMIZED: QWidget_showMaximized(Widget);
525553 SW_HIDE: QWidget_setVisible(Widget, False);
526554 end;
527555
528556 Result := True;
529557end;
530558```
531559
532560#### Видимость для элементов управления
533561
534562Для элементов управления внутри формы вам необходимо реализовать функцию класса TMyWSWinControl.ShowHide, которая находится в классе TMyWSWinControl в файле mywscontrols.pp
535563
536564Помните, что большинство элементов управления наследуются от TWinControl, поэтому реализация этой функции гарантирует, что свойство Visible реализовано для всех стандартных элементов управления, которые его имеют. Ниже приведен пример кода для набора виджетов Qt.
537565
538566``` pascal
539567{------------------------------------------------------------------------------
540568 Method: TQtWSWinControl.ShowHide
541569 Params: AWinControl - the calling object
542570
543571 Returns: Nothing
544572
545573 Shows or hides a widget.
546574 ------------------------------------------------------------------------------}
547575class procedure TQtWSWinControl.ShowHide(const AWinControl: TWinControl);
548576begin
549577 if AWinControl = nil then exit;
550578
551579 if not AWinControl.HandleAllocated then exit;
552580
553581 if AWinControl.HandleObjectShouldBeVisible then
554582 QWidget_setVisible(TQtWidget(AWinControl.Handle).Widget, True)
555583 else QWidget_setVisible(TQtWidget(AWinControl.Handle).Widget, False);
556584end;
557585```
558586
559587### Реализация компонентов на основе TStrings
560588
561589Некоторые компоненты используют TStrings для хранения отображаемой информации, например: TCustomMemo, TCustomListBox и TCustomComboBox.
562590
563591Для их реализации недостаточно просто реализовать их функции, например, в классе TQtCustomMemo. Одна из реализуемых функций будет называться GetStrings и выглядеть следующим образом:
564592
565593``` pascal
566594class function TQtWSCustomListBox.GetStrings(const ACustomListBox: TCustomListBox): TStrings;
567595var
568596 ListWidgetH: QListWidgetH;
569597begin
570598 ListWidgetH := QListWidgetH((TQtWidget(ACustomListBox.Handle).Widget));
571599 Result := TQtListStrings.Create(ListWidgetH, ACustomListBox);
572600end;
573601```
574602
575603Эта функция должна возвращать потомок TStrings, предназначенный для отслеживания, что строки добавляются или удаляются из списка, и отправлять эту информацию в набор виджетов для обновления элемента управления. Например, следующее объявление показывает, как выглядит TQtListStrings:
576604
577605``` pascal
578606 TQtListStrings = class(TStrings)
579607 private
580608 FListChanged: Boolean; // StringList и QtListWidget не синхронизированы
581609 FStringList: TStringList; // Содержит пункты для показа
582610 FQtListWidget: QListWidgetH; // Qt Widget
583611 FOwner: TWinControl; // Lazarus'овский владелец элементов управления ListStrings
584612 FUpdating: Boolean; // Мы меняем Qt Widget
585613 procedure InternalUpdate;
586614 procedure ExternalUpdate(var Astr: TStringList; Clear: Boolean = True);
587615 procedure IsChanged; // событие OnChange, вызваемое действием программы
588616 protected
589617 function GetTextStr: string; override;
590618 function GetCount: integer; override;
591619 function Get(Index : Integer) : string; override;
592620 //procedure SetSorted(Val : boolean); virtual;
593621 public
594622 constructor Create(ListWidgetH : QListWidgetH; TheOwner: TWinControl);
595623 destructor Destroy; override;
596624 procedure Assign(Source : TPersistent); override;
597625 procedure Clear; override;
598626 procedure Delete(Index : integer); override;
599627 procedure Insert(Index : integer; const S: string); override;
600628 procedure SetText(TheText: PChar); override;
601629 //procedure Sort; virtual;
602630 public
603631 //property Sorted: boolean read FSorted write SetSorted;
604632 property Owner: TWinControl read FOwner;
605633 function ListChangedHandler(Sender: QObjectH; Event: QEventH): Boolean; cdecl;
606634 end;
607635```
608636
609637Вы можете увидеть его реализацию в модуле qtobjects.pas интерфейса qt.
610638
611639### Реализация меню
612640
613641Меню доступны на LCL для создания основных меню или всплывающих меню. TMenu является владельцем более крупной структуры меню со многими пунктами. Элементы могут иметь подэлементы, и не нуждаются в дополнительном TMenus.
614642
615643Также помните, что в LCL дескриптор создается только при необходимости, и в это время все свойства элементов управления уже инициализированы. Это очень помогает для наборов виджетов, где в зависимости от свойств элемента меню он может принадлежать к одному или другому классу, например, Qt.
616644
617645Следующие вещи должны быть реализованы для того, чтобы меню работали:
618646
6196471\) Все методы модуля QtWSMenus, которые будут реализовывать создание и изменение меню
620648
6216492\) функция `TWinCEWidgetSet.SetMenu(AWindowHandle: HWND; AMenuHandle: HMENU): Boolean;` из файла wincewinapi.inc, в котором будет реализована поддержка главного меню, связанного с окном.
622650
623651#### Порядок создания меню
624652
625653При реализации меню важно понимать, в каком порядке они создаются. Например, мы хотим создать следующую структуру меню:
626654
627655![](/wiki-images/6/69/Menu_creation_order.png)
628656
629657И когда наше приложение будет выполнено, при каждом вызове TQtWSMenuItem.CreateHandle будет появляться сообщение 'Creating MenuItem'(Создание MenuItem) с заголовком меню и сообщение 'Creating Menu'(Создание меню) с названием меню (потомки TMenu не имеют подпись), каждый раз, когда вызывается TQtWSMenu.CreateHandle.
630658
631659Вот результирующий вывод такого программного обеспечения:
632660
633-` Creating Menu. Name: MainMenu1`\
634-` Creating MenuItem: Item1           Parent=Menu.Items : TMenuItem`\
635-` Creating MenuItem: SubItem11       Parent=Item1      : TMenuItem`\
636-` Creating MenuItem: SubItem12       Parent=Item1      : TMenuItem`\
637-` Creating MenuItem: SubItem13       Parent=Item1      : TMenuItem`\
638-` Creating MenuItem: SubItem14       Parent=Item1      : TMenuItem`\
639-` Creating MenuItem: SubSubItem141   Parent=SubItem14  : TMenuItem`\
640-` Creating MenuItem: SubSubItem142   Parent=SubItem14  : TMenuItem`\
641-` Creating MenuItem: SubSubItem143   Parent=SubItem14  : TMenuItem`\
642-` Creating MenuItem: SubSubItem144   Parent=SubItem14  : TMenuItem`\
643-` Creating MenuItem: Item2           Parent=Menu.Items : TMenuItem`\
644-` Creating MenuItem: SubItem21       Parent=Item2      : TMenuItem`\
645-` Creating MenuItem: SubItem22       Parent=Item2      : TMenuItem`\
646-` Creating MenuItem: SubItem23       Parent=Item2      : TMenuItem`\
647-` Creating MenuItem: Item3           Parent=Menu.Items : TMenuItem`\
648-` Creating MenuItem: Item4           Parent=Menu.Items : TMenuItem`
661+```text
662+ Creating Menu. Name: MainMenu1
663+ Creating MenuItem: Item1 Parent=Menu.Items : TMenuItem
664+ Creating MenuItem: SubItem11 Parent=Item1 : TMenuItem
665+ Creating MenuItem: SubItem12 Parent=Item1 : TMenuItem
666+ Creating MenuItem: SubItem13 Parent=Item1 : TMenuItem
667+ Creating MenuItem: SubItem14 Parent=Item1 : TMenuItem
668+ Creating MenuItem: SubSubItem141 Parent=SubItem14 : TMenuItem
669+ Creating MenuItem: SubSubItem142 Parent=SubItem14 : TMenuItem
670+ Creating MenuItem: SubSubItem143 Parent=SubItem14 : TMenuItem
671+ Creating MenuItem: SubSubItem144 Parent=SubItem14 : TMenuItem
672+ Creating MenuItem: Item2 Parent=Menu.Items : TMenuItem
673+ Creating MenuItem: SubItem21 Parent=Item2 : TMenuItem
674+ Creating MenuItem: SubItem22 Parent=Item2 : TMenuItem
675+ Creating MenuItem: SubItem23 Parent=Item2 : TMenuItem
676+ Creating MenuItem: Item3 Parent=Menu.Items : TMenuItem
677+ Creating MenuItem: Item4 Parent=Menu.Items : TMenuItem
678+```
649679
650680Для всех MenuItems можно использовать GetParentMenu, чтобы получить владельца их родителя: Menu (TMainMenu).
651681
652682### Включение/отключение элемента управления
653683
654684Текущий способ установить включение/отключение элемента управления - реализация winapi EnableWindow. Этот API должен работать в общем на любом элементе управления. Он должен включать/отключать ввод с помощью мыши и клавиатуры для указанного окна или элемента управления, а также помечать его как недоступный для редактирования пользователем, например, делая его серым.
655685
656686``` pascal
657687{------------------------------------------------------------------------------
658688 Method: EnableWindow
659689 Params: HWnd - handle to window / дескриптор окна
660690 BEnable - whether to enable the window / нужно ли включать окно
661691 Returns: If the window was previously disabled / возращаемый результат: если окно было ранее отключено
662692
663693 Enables or disables mouse and keyboard input to the specified window or
664694 control / Включает или отключает ввод с клавиатуры и мыши в указанное окно или
665695  элемент управления
666696 ------------------------------------------------------------------------------}
667697function TWin32WidgetSet.EnableWindow(hWnd: HWND; bEnable: Boolean): Boolean;
668698```
669699
670700### Фигурные окна
671701
672702Окна могут быть сформированы на основе растрового изображения (TBitmap) или региона(TRegion). Регион является видимой частью
673703
674704Чтобы реализовать фигурные окна на основе TBitmap, реализуйте TWSWinControl.setShape
675705
676706Для реализации фигурных окон на основе TRegion реализуйте LCLIntf.SetWindowRgn
677707
678708См.также: [[LCL Tips/ru|Создание не прямоугольных окон или элементов управления]]
679709
680710### Системные цвета
681711
682712Некоторые константы цвета на самом деле являются системными цветами, такими как clBtnFace, clForm, clWindow и проч. и проч.
683713
684714Для реализации поддержки системных цветов должна быть реализована подпрограмма WinAPI GetSysColor:
685715
686716``` pascal
687717function GetSysColor(nIndex: Integer): DWORD; override;
688718```
689719
690720А вот фрагмент цветовых констант, которые должны поддерживаться. Проверьте LCLType для последних значений:
691721
692722``` pascal
693723//==============================================
694724// API system Color constants pbd
695725// note these are usually shown ORed with
696726// $80000000 as these would have interfered with
697727// other MS color enumerations
698728// GetSysColor and SetSysColor expects the values
699729// below
700730//==============================================
701731
702732type
703733 COLORREF = LongInt;
704734 TColorRef = COLORREF;
705735
706736const
707737 CLR_INVALID = TColorRef($FFFFFFFF);
708738
709739 COLOR_SCROLLBAR = 0;
710740 COLOR_BACKGROUND = 1;
711741 COLOR_ACTIVECAPTION = 2;
712742 COLOR_INACTIVECAPTION = 3;
713743 COLOR_MENU = 4;
714744 COLOR_WINDOW = 5;
715745 COLOR_WINDOWFRAME = 6;
716746 COLOR_MENUTEXT = 7;
717747 COLOR_WINDOWTEXT = 8;
718748 COLOR_CAPTIONTEXT = 9;
719749 COLOR_ACTIVEBORDER = 10;
720750 COLOR_INACTIVEBORDER = 11;
721751 COLOR_APPWORKSPACE = 12;
722752 COLOR_HIGHLIGHT = 13;
723753 COLOR_HIGHLIGHTTEXT = 14;
724754 COLOR_BTNFACE = 15;
725755 COLOR_BTNSHADOW = 16;
726756 COLOR_GRAYTEXT = 17;
727757 COLOR_BTNTEXT = 18;
728758 COLOR_INACTIVECAPTIONTEXT = 19;
729759 COLOR_BTNHIGHLIGHT = 20;
730760 COLOR_3DDKSHADOW = 21;
731761 COLOR_3DLIGHT = 22;
732762 COLOR_INFOTEXT = 23;
733763 COLOR_INFOBK = 24;
734764 // PBD: 25 is unassigned in all the docs I can find
735765 // if someone finds what this is supposed to be then fill it in
736766 // note defaults below, and cl[ColorConst] in graphics
737767 COLOR_HOTLIGHT = 26;
738768 COLOR_GRADIENTACTIVECAPTION = 27;
739769 COLOR_GRADIENTINACTIVECAPTION = 28;
740770 COLOR_MENUHILIGHT = 29;
741771 COLOR_MENUBAR = 30;
742772
743773 COLOR_FORM = 31;
744774
745775 COLOR_ENDCOLORS = COLOR_FORM;
746776
747777 COLOR_DESKTOP = COLOR_BACKGROUND;
748778 COLOR_3DFACE = COLOR_BTNFACE;
749779 COLOR_3DSHADOW = COLOR_BTNSHADOW;
750780 COLOR_3DHIGHLIGHT = COLOR_BTNHIGHLIGHT;
751781 COLOR_3DHILIGHT = COLOR_BTNHIGHLIGHT;
752782 COLOR_BTNHILIGHT = COLOR_BTNHIGHLIGHT;
753783
754784 MAX_SYS_COLORS = COLOR_ENDCOLORS;
755785 SYS_COLOR_BASE = TColorRef($80000000);
756786```
757787
758788### ShowMessage
759789
760790Эти стандартные диалоги реализованы исключительно в LCL в следующих местах:
761791
762792- Файл класса TPromptDialog lcl/include/promptdialog.inc
763793
764794### SpinEdit
765795
766796И TFloatSpinEdit, и TSpinEdit реализованы в классе TWSFloatSpinEdit.
767797
768798### Буфер обмена
769799
770800Поддержка буфера обмена реализована в lclintf путем реализации подпрограмм Windows API. Это процедуры:
771801
772802``` pascal
773803function ClipboardFormatToMimeType(FormatID: TClipboardFormat): string; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
774804function ClipboardGetData(ClipboardType: TClipboardType;
775805 FormatID: TClipboardFormat; Stream: TStream): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
776806// ! ClipboardGetFormats: List будет создан. Вы должны освободить его самостоятельно с помощью FreeMem(List) !
777807function ClipboardGetFormats(ClipboardType: TClipboardType;
778808 var Count: integer; var List: PClipboardFormat): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
779809function ClipboardGetOwnerShip(ClipboardType: TClipboardType;
780810 OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
781811 Formats: PClipboardFormat): boolean; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
782812function ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
783813```
784814
785815Последовательность вызовов при запуске программы:
786816
787817``` pascal
788818TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/bmp Result=51027040
789819TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/delphi.bitmap Result=0
790820TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/xpm Result=0
791821TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/png Result=0
792822TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=image/jpeg Result=0
793823```
794824
795825Последовательность вызовов во время присвоения "Caption:= Clipboard.AsText":
796826
797827``` pascal
798828TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=text/plain Result=51026976
799829TCocoaWidgetSet.ClipboardGetData ClipboardType=clipboard FormatID: 51026976
800830```
801831
802832### TSpeedButton
803833
804834Этот элемент управления является потомком TGraphicControl, поэтому он не имеет дескриптора и рисуется сам. Чтобы сохранить собственный внешний вид, он использует функцию `ThemeServices.DrawElement(Details.Element=teButton)` для отрисовки самого себя, поэтому реализуйте эту функцию, чтобы этот элемент управления работал. Естественно, многие другие DC, WindowOrg, рисование текста и рисование должны работать, как объяснено для TLabel.
805835
806836### TRadioButton и TCheckButton
807837
808838**Сообщения:**
809839
810840В основном Widgetset должен делать:
811841
812842- Отправлять LM_CHANGE, когда радиокнопка не отмечена/отмечена (LCL позаботится о том, будет ли вызываться OnClick или нет)
813843- Не отправлять LM_CHANGE, когда вызывается TWSCustomCheckBox.SetState(SetChecked)
814844
815845См. также этот отчет об ошибке: <http://bugs.freepascal.org/view.php?id=13939>
816846
817847### Поддержка FullScreen
818848
819849FullScreen(окно на весь экран без рамки - прим.перев.) реализован как состояние окна wsFullScreen и, как таковой, реализован в LCLIntf.ShowWindow, где должна обрабатываться константа SW_SHOWFULLSCREEN.
820850
821851## Пример работы интерфейсов LCL
822852
823853Ниже приведен простой пример. Предположим, вы разработали компонент trayicon. Как бы вы реализовали его в LCL для правильной работы на различных поддерживаемых платформах?
824854
825855Вам нужно будет сгенерировать следующие файлы:
826856
827857 \trayicon.pas
828858 \wstrayicon.pas
829859 \gtk\gtkwstrayicon.pas
830860 \gtk\trayintf.pas
831861 \win32\win32wstrayicon.pas
832862 \win32\trayintf.pas
833863
834864Предоставление отдельных файлов wsXXX.pas и \\\$(LCLWidgetType)\XXXintf.pas полностью исключает необходимость использования ifdefs. Вам нужно будет добавить в качестве пути модуля \$(LCLWidgetType) в файл XXXintf.pas, чтобы инициализировать корректный файл trayintf.pas, который, в свою очередь, инициализирует корректный класс WS Tray.
835865
836866Внутри trayicon.pas вы включаете wstrayicon. Извлеките свой основной класс из класса LCL и используйте только wstrayicon в его реализации. Все классы LCL, которые взаимодействуют с набором виджетов, должны быть получены из [[doc:/lcl/lclclasses/tlclcomponent.html|TLCLComponent]], объявленного в модуле [[doc:/lcl/lclclasses|LCLClasses]].
837867
838868``` pascal
839869unit TrayIcon;
840870
841871interface
842872
843873type
844874 TTrayIcon = class(TLCLComponent)
845875 public
846876 procedure DoTray;
847877 end;
848878
849879implementation
850880
851881uses wstrayicon;
852882
853883procedure TTrayIcon.DoTray;
854884begin
855885 // вызов wstrayicon
856886end;
857887
858888end.
859889```
860890
861891в trayintf вы используете gtkwstrayicon или win32trayicon, в зависимости от того, какой это файл trayintf.
862892
863893в wstrayicon вы создаете класс следующим образом:
864894
865895``` pascal
866896unit WSTrayIcon;
867897
868898uses WSLCLClasses, Controls, TrayIcon; // и другие модули
869899
870900TWSTrayIconClass = class of TWSTrayIcon;
871901TWSTrayIcon = class(TWSWinControl);
872902public
873903 // все они должны быть виртуальными и классовыми процедурами !!
874904 class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;
875905 class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;
876906 ....
877907end;
878908...
879909
880910implementation
881911
882912procedure TWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
883913begin
884914 //ничего не делаем
885915end;
886916
887917procedure TWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
888918begin
889919 //ничего не делаем
890920end;
891921```
892922
893923теперь в gtkwstrayicon.pas сделайте так:
894924
895925``` pascal
896926uses WSTrayIcon, WSLCLClasses, Controls, TrayIcon, gtk, gdk;
897927
898928TGtkWSTrayIcon = class(TWSTrayIcon);
899929private
900930 class function FindSystemTray(const ATrayIcon: TCustomTrayIcon): TWindow; virtual;public
901931 class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
902932 class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
903933 class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override;
904934 ....
905935end;
906936...
907937
908938implementation
909939
910940procedure TGtkWSTrayIcon.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
911941var
912942WidgetInfo: PWidgetInfo;
913943begin
914944 Result := gtk_plug_new;
915945 WidgetInfo := CreateWidgetInfo(AWinControl, Result); // как-то так или иначе
916946 TGtkWSWincontrolClass(WidgetSetClass).SetCallbacks(AWinControl);
917947 // и многое другое
918948end;
919949
920950function TGtkWSTrayIcon.FindSystemTray(const ATrayIcon:
921951TCustomTrayIcon): TWindow;
922952begin
923953 // что-то делаем
924954end;
925955
926956procedure TGtkWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
927957var
928958SystemTray: TWindow;
929959begin
930960 SystemTray := FindSystemTray(ATrayIcon);
931961 //что-то делаем
932962end;
933963
934964procedure TGtkWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
935965begin
936966 //что-то делаем
937967end;
938968
939969......
940970
941971initialization
942972
943973RegisterWSComponent(TCustomTrayIcon, TGtkWSTrayIcon); //вот это очень важно!!!
944974
945975end.
946976```
947977
948978затем наконец-то в trayicon.pas вы пишете как обычно
949979
950980``` pascal
951981uses WSTrayIcon; //и т.д., вы НЕ включаете сюда GtkWSTrayIcon!
952982
953983TCustomTrayIcon = class(TWinControl)
954984public
955985 procedure EmbedControl;
956986....
957987end;
958988
959989...
960990procedure TTrayIcon.EmbedControl;
961991begin
962992 TWSTrayIconClass(WidgetSetClass).EmbedControl(Self);
963993end;
964994```
965995
966996------------------------------------------------------------------------
967997
968998Этот документ находится в стадии разработки. Вы можете помочь, написав разделы этого документа. Если вы не можете найти информацию, которую вы ищете в этом документе, пожалуйста, добавьте свой вопрос на страницу обсуждения. Это поможет нам писать документацию на уровне, который не является ни слишком простым, ни слишком сложным, и полностью охватывает области, о которых люди хотят знать.
969999
9701000---
9711001*From the Free Pascal wiki, page "LCL Internals/ru", as it stood on 2020-02-19 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history