Sign in Join
Wiki copytemporary

LCL Internals/ja: what changed

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

Title: LCL Internals/ja

OldNewLine
1+## LCLの内部
2+
3+LCLとその「インタフェース」があります。 LCLはプラットホーム独立を担う部分です。そして、それはlazarus/lcl/ディレクトリにあります。 このディレクトリは主要なクラス定義を含んでいます。 多くのさまざまなコントロールが、様々な.incファイルの中のlazarus/lcl/include/ディレクトリに実装されています。これは、より速く特定のコントロールの実装を見つけるためです。TCustomMemoを例にとると、それは、custommemo.incにあります。 どの.incファイルも、{%MainUnit...}ではじまり、どこでそれがインクルードされているかを定義しています。
4+
5+そして、lazarus/lcl/interfaces/内のサブディレクトリに、「インターフェース」があります。 gtkインターフェースは、gtk/以下に、Win32インターフェースは win32/以下に、というふうに。 それらすべては、LCLで使われ、メインのインターフェースオブジェクトで生成されるインターフェースユニットを持っています。 たいてい、メインのインターフェースオブジェクトは、XXint.pp(win32int.ppなど)で定義されており、いろんなincファイルで実装されています。 XXobject.incは、特別なメソッドのインターフェースです。 XXwinapi.incは、winapiの実装メソッドです。 XXlistsl.incは、TComboBox,TListBoxなどのコントロールで使われるStringListの実装です。 XXcallback.incはウイジェットイベントのハンドリングと、適切にLCLへ通知をおこなうものです。
6+
7+すべてのコントロールは、WidgetSetClassプロパティをもっています。これは、インターフェースディレクトリのミラーのクラスです。たとえば、TCustomEditのミラーは、TWSCustomEditです。 このメソッドは、win32wsstdctrlsの内部のTWin32WSCustomEditで実装されています。 これは、LCLがインターフェースと対話する方法で、インターフェースが何か仕事をする方法です。
8+
9+インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(\<message_id\>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。
10+
11+## 新しいWidgetsetを生成する方法
12+
13+これは、新しいwidgetsetを開発する初歩的なチュートリアルです。 この方法は、新しいqt4インターフェースを作るときの私の経験によるものです。
14+
15+どうして新しいWidgetsetを追加したくなるのでしょうか。もっと多くのプラットフォームのサポートを追加するために!なぜでしょう。 基本的には、再コンパイルさえすれば、他のいろんなプラットフォームで動作するという、Lazarusのもっとも有利な点を改善するためです。 だから、新しいwidgetsetを追加したいんだ、と言ってみましょう。 まず最初に、widgetのpascalバインディングを取得し、どのようにそれを使うか、知る必要があります。 これは数時間ネット上の基本的なチュートリアルをやってもらえば、それほど難しいことではありません。 それらのチュートリアルをpascalに翻訳すれば、それが導入には充分です。
16+
17+Qt4を使うとき、私はパスカル向けのDen Jean qt4 bindingsを使いました。そして、とても基本的なプログラムをしました。これがそのプログラムです。
18+
19+ program qttest;
20+
21+ uses qt4;
22+
23+ var
24+ App: QApplicationH;
25+ MainWindow: QMainWindowH;
26+ begin
27+ App := QApplication_Create(@argc,argv);
28+
29+ MainWindow := QMainWindow_Create;
30+
31+ QWidget_show(MainWindow);
32+
33+ QApplication_Exec;
34+ end.
35+
36+上のプログラムはqt4のプログラムです。これで、Qtプログラムが出来てしまうなんて、素敵ですね。 でも、ちょっとまって下さい。これが本当につくりたかったものではないんです。 私がやりたいのは、すでに存在するアプリケーションを再コンパイルして、Qtとリンクするだけのものですよ ! 私は上のアプリケーションを次のようにします。
37+
38+ program qttest;
39+
40+ {$mode objfpc}{$H+}
41+
42+ uses
43+ Interfaces, Classes, Forms,
44+ { Add your units here }
45+ qtform;
46+
47+ begin
48+ Application.Initialize;
49+ Application.CreateForm(TForm1, Form1);
50+ Application.Run;
51+ end.
52+
53+こうすることで、フォームがLazarus IDEでメンテナンスできて、ビジュアルな設計が可能になります。 それでは、Qt用のLCLのWidgetsetをつくってみます。
54+
55+最初に新しいwidgetsetでやることは、空っぽの骨組みをつくることです。 初期のqtやcarbonのようなwidgetsetsの開発では、骨組みからはじめます。
56+
57+多くのwidgetsのファイルを注意深くみると、lclのInterfaces.pasからコールされる最初にファイルを見つけることができます。 このファイルは他のQtInt.pasやそういったファイルをコールするだけです。 QtInt.pasには私達が実装すべきTWidgetSetクラスのためのコードがあります。 空っぽの骨組みでは、この関数が実装されるべきいろんな関数を持っていることに気が付くでしょう。
58+
59+ TQtWidgetSet = Class(TWidgetSet)
60+ private
61+ App: QApplicationH;
62+ public
63+ {$I qtwinapih.inc}
64+ {$I qtlclintfh.inc}
65+ public
66+ // Application
67+ procedure AppInit(var ScreenInfo: TScreenInfo); override;
68+ procedure AppRun(const ALoop: TApplicationMainLoop); override;
69+ procedure AppWaitMessage; override;
70+ procedure AppProcessMessages; override;
71+ procedure AppTerminate; override;
72+ procedure AppMinimize; override;
73+ procedure AppBringToFront; override;
74+ public
75+ constructor Create;
76+ destructor Destroy; override;
77+ function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override;
78+ procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override;
79+ procedure DCRedraw(CanvasHandle: HDC); override;
80+ procedure SetDesigning(AComponent: TComponent); override;
81+
82+ function InitHintFont(HintFont: TObject): Boolean; override;
83+
84+ // create and destroy
85+ function CreateComponent(Sender : TObject): THandle; override; // deprecated
86+ function CreateTimer(Interval: integer; TimerFunc: TFNTimerProc): integer; override;
87+ function DestroyTimer(TimerHandle: integer): boolean; override;
88+ end;
89+
90+### 新しいウインドウコンポーネントを実装する方法
91+
92+ウインドウコンポーネントは、TWinControlからの派生です。 これらのコントロールはHandleなどといったものを持っていて、Widgetsetから作られるべきです。新しいウインドウコンポーネントをwidgetsetに追加するのは簡単です。
93+
94+それでは、Qt WidgetsetにTQtWSCustomEditを追加してみましょう。
95+
96+TCustomEditからはじめるために、StdCtrlsユニットのTWinControlの派生にすることです。
97+
98+QtWSStrCtrlsユニットのTQtWSCustomEditの宣言を見てみましょう。
99+
100+ TQtWSCustomEdit = class(TWSCustomEdit)
101+ private
102+ protected
103+ public
104+ end;
105+
106+TWSCustomEditで宣言されている静的メソッドを追加し、オーバーライドします。コードは次のようになるでしょう。
107+
108+ TQtWSCustomEdit = class(TWSCustomEdit)
109+ private
110+ protected
111+ public
112+ class function CreateHandle(const AWinControl: TWinControl;
113+ const AParams: TCreateParams): HWND; override;
114+ class procedure DestroyHandle(const AWinControl: TWinControl); override;
115+ { class function GetSelStart(const ACustomEdit: TCustomEdit): integer; override;
116+ class function GetSelLength(const ACustomEdit: TCustomEdit): integer; override;
117+
118+ class procedure SetCharCase(const ACustomEdit: TCustomEdit; NewCase: TEditCharCase); override;
119+ class procedure SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); override;
120+ class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
121+ class procedure SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char); override;
122+ class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
123+ class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
124+ class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
125+
126+ class procedure GetPreferredSize(const AWinControl: TWinControl;
127+ var PreferredWidth, PreferredHeight: integer); override;}
128+ end;
129+
130+コードのコメントされた部分はTCustomEditにとって完全に機能するように実装する必要のある関数です。CreateHandleとDestroyHandleだけは、フォームに表示して編集できるようにするために、必要充分なことをします。これで、この記事でやることを充分満たしています。
131+
132+CTRL+SHIFT+Cを押して、コード補完をします。そして、CreateHandleとDestroyHandleを実装します。Qt4の場合は次のようになります。
133+
134+ { TQtWSCustomEdit }
135+
136+ class function TQtWSCustomEdit.CreateHandle(const AWinControl: TWinControl;
137+ const AParams: TCreateParams): HWND;
138+ var
139+ Widget: QWidgetH;
140+ Str: WideString;
141+ begin
142+ // Creates the widget
143+ WriteLn('Calling QTextDocument_create');
144+ Str := WideString((AWinControl as TCustomMemo).Lines.Text);
145+ Widget := QTextEdit_create(@Str, QWidgetH(AWinControl.Parent.Handle));
146+
147+ // Sets it's initial properties
148+ QWidget_setGeometry(Widget, AWinControl.Left, AWinControl.Top,
149+ AWinControl.Width, AWinControl.Height);
150+
151+ QWidget_show(Widget);
152+
153+ Result := THandle(Widget);
154+ end;
155+
156+ class procedure TQtWSCustomEdit.DestroyHandle(const AWinControl: TWinControl);
157+ begin
158+ QTextEdit_destroy(QTextEditH(AWinControl.Handle));
159+ end;
160+
161+そして、ユニットの下のほうの"RegisterWSComponent(TCustomEdit, TQtWSCustomEdit);"といった部分のコメントを外します。
162+
163+これで、TCustomEditをフォームの下のほうへドロップすることができ、動くことが期待できます。
164+
165+
166+^)
167+
168+### TBitmapを実装する
169+
170+TBitmapや他のグラフィカルなオブジェクトを実装することは、コメントがされていないqtwinapi.pasファイルの特別な関数を使っているので、骨が折れる作業かもしれません。
171+
172+しかし、下のコードをコンパイルしてみてください。
173+
174+ var
175+ Bitmap: TBitmap;
176+ begin
177+ Bitmap := TBitmap.Create;
178+ try
179+ Bitmap.LoadFromFile('myfile.bmp');
180+ Canvas.Draw(Bitmap, 0, 0);
181+ finally
182+ Bitmap.Free;
183+ end;
184+ end;
185+
186+次のものは、実行時、上のコードでwidgetsetインターフェースがコールされるときに、関数が呼ばれる順序です。
187+
188+1 - GetDC(0);
189+
190+デバイスコンテキストを生成するだけです。
191+
192+2 - GetDeviceRawImageDescription
193+
194+Qtで内部のピクセルフォーマットを記述するユーティリティです。
195+
196+3 - CreateBitmapFromRawImage
197+
198+ここでネイティブなイメージオブジェクトを生成する必要があり、RawDataからロードします。 イメージデータに関する情報は、2でおこなったピクセルフォーマットに基づいて格納されています。
199+
200+## インターフェースがどのように機能するか示す例
201+
202+下記は単純な例です。あるボタンコンポーネントを思い浮かべてください。 どうやってLCLでは別々のプラットフォームに対して実装されているのでしょうか。
203+
204+下記のファイルがあると思います。
205+
206+\trayicon.pas
207+
208+\wstrayicon.pas
209+
210+\gtk\gtkwstrayicon.pas
211+
212+\gtk\trayintf.pas
213+
214+\win32\win32wstrayicon.pas
215+
216+\win32\trayintf.pas
217+
218+この方法で、ifdefをなくすようにできます。
219+
220+正しいWSTray classを初期化するためのtrayinf.pasを加えるために、ユニットパス に\$(LCLWidgetType)を加える必要があります。
221+
222+trayicon.pasの中に、wstrayiconをインクルードします。LCLクラスから派生してメインクラスをつくって、wstrayiconをimplementation部でusesしてください。widgetsetと通信できるすべてのLCLクラスは、[[doc:/lcl/lclclasses|LCLClasses]] ユニットで宣言されている[[doc:/lcl/lclclasses/tlclcomponent.html|TLCLComponentから]]派生されています。
223+
224+ unit TrayIcon;
225+
226+ interface
227+
228+ type
229+ TTrayIcon = class(TLCLComponent)
230+ public
231+ procedure DoTray;
232+ end;
233+
234+ implementation
235+
236+ uses wstrayicon;
237+
238+ procedure TTrayIcon.DoTray;
239+ begin
240+ // Call wstrayicon
241+ end;
242+
243+ end.
244+
245+trayintfの内部で、trayintfファイルであるgtkwstrayiconか、win32trayiconを使います。 wstrayiconの内部で、次のようにクラスを作ります。
246+
247+ unit WSTrayIcon;
248+
249+ uses WSLCLClasses, Controls, TrayIcon; // and other things as well
250+
251+ TWSTrayIcon = class of TWSTrayIcon;
252+ TWSTrayIcon = class(TWSWinControl);
253+ public
254+ class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
255+ virtual; // these must all be virtual and class procedures!!
256+ class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual;
257+ ....
258+ end;
259+ ...
260+
261+ implementation
262+
263+ procedure TWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
264+ begin
265+ //do nothing
266+ end;
267+
268+ procedure TWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
269+ begin
270+ //do nothing
271+ end;
272+
273+gtkwstrayicon.pasでは、このようにしています。
274+
275+ uses WSTrayIcon, WSLCLClasses, Controls, TrayIcon, gtk, gdk;
276+
277+ TGtkWSTrayIcon = class(TWSTrayIcon);
278+ private
279+ class function FindSystemTray(const ATrayIcon: TCustomTrayIcon):
280+ TWindow; virtual;
281+ public
282+ class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); override;
283+ class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
284+ override;
285+ class function CreateHandle(const AWinControl: TWinControl; const
286+ AParams: TCreateParams): HWND; override;
287+ ....
288+ end;
289+ ...
290+
291+ implementation
292+
293+ procedure TGtkWSTrayIcon.CreateHandle(const AWinControl: TWinControl;
294+ const AParams: TCreateParams): HWND;
295+ var
296+ WidgetInfo: PWidgetInfo;
297+ begin
298+
299+ Result := gtk_plug_new;
300+ WidgetInfo := CreateWidgetInfo(AWinControl, Result); // it's something
301+ like this anyway
302+ TGtkWSWincontrolClass(WidgetSetClass).SetCallbacks(AWinControl);
303+ // and more stuff
304+ end;
305+
306+ function TGtkWSTrayIcon.FindSystemTray(const ATrayIcon:
307+ TCustomTrayIcon): TWindow;
308+ begin
309+ // do something
310+ end;
311+
312+ procedure TGtkWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon);
313+ var
314+ SystemTray: TWindow;
315+ begin
316+ SystemTray := FindSystemTray(ATrayIcon);
317+ //do something
318+ end;
319+
320+ procedure TGtkWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon);
321+ begin
322+ //do something
323+ end;
324+
325+ ......
326+
327+ initialization
328+
329+ RegisterWSComponent(TCustomTrayIcon, TGtkWSTrayIcon); //this is very
330+ important!!!
331+
332+ end.
333+
334+そうすると、trayicon.pasは最終的には次のようになります。
335+
336+ uses WSTrayIcon; //etc. you DON'T include GtkWSTrayIcon here!
337+
338+ TCustomTrayIcon = class(TWinControl)
339+ public
340+ procedure EmbedControl;
341+ ....
342+ end;
343+
344+ ...
345+ procedure TTrayIcon.EmbedControl;
346+ begin
347+ TWSTrayIconClass(WidgetSetClass).EmbedControl(Self);
348+
349+ end;
350+
351+------------------------------------------------------------------------
352+
353+このドキュメントは作業途中です。このドキュメントの章を書く手助けをしていただいてかまいません。 もし、このドキュメントの中で何か情報を探していて見つからなかった場合には、質問をdiscussion pageに入れてください。そうすることが、読みたいみなさんのレベルにあわせて、あまり過不足のないドキュメントを書く手助けになります。.
354+
355+---
356+*From the Free Pascal wiki, page "LCL Internals/ja", as it stood on 2012-02-03 after a change by Chronos, 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).*
357+Category: LCL/ja

Back to history