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
| Old | New | Line | |
|---|---|---|---|
| 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 |
