Difference between revisions of "LCL Internals/ja"
Line 208: | Line 208: | ||
:^) | :^) | ||
− | === | + | ===TBitmapを実装する=== |
− | + | TBitmapや他のグラフィカルなオブジェクトを実装することは、コメントがされていないqtwinapi.pasファイルの特別な関数を使っているので、骨が折れる作業かもしれません。 | |
− | + | しかし、下のコードをコンパイルしてみてください。 | |
<pre> | <pre> | ||
Line 228: | Line 228: | ||
</pre> | </pre> | ||
− | + | 次のものは、実行時、上のコードでwidgetsetインターフェースがコールされるときに、関数が呼ばれる順序です。 | |
1 - GetDC(0); | 1 - GetDC(0); | ||
− | + | デバイスコンテキストを生成するだけです。 | |
2 - GetDeviceRawImageDescription | 2 - GetDeviceRawImageDescription | ||
− | + | Qtで内部のピクセルフォーマットを記述するユーティリティです。 | |
3 - CreateBitmapFromRawImage | 3 - CreateBitmapFromRawImage | ||
− | + | ここでネイティブなイメージオブジェクトを生成する必要があり、RawDataからロードします。 | |
+ | イメージデータに関する情報は、2でおこなったピクセルフォーマットに基づいて格納されています。 | ||
==Example of how the interfaces work== | ==Example of how the interfaces work== |
Revision as of 07:54, 16 August 2006
│
English (en) │
español (es) │
日本語 (ja) │
русский (ru) │
LCLの内部
LCLとその「インタフェース」があります。 LCLはプラットホーム独立を担う部分です。そして、それはlazarus/lcl/ディレクトリにあります。 このディレクトリは主要なクラス定義を含んでいます。 多くのさまざまなコントロールが、様々な.incファイルの中のlazarus/lcl/include/ディレクトリに実装されています。これは、より速く特定のコントロールの実装を見つけるためです。TCustomMemoを例にとると、それは、custommemo.incにあります。 どの.incファイルも、{%MainUnit...}ではじまり、どこでそれがインクルードされているかを定義しています。
そして、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へ通知をおこなうものです。
すべてのコントロールは、WidgetSetClassプロパティをもっています。これは、インターフェースディレクトリのミラーのクラスです。たとえば、TCustomEditのミラーは、TWSCustomEditです。 このメソッドは、win32wsstdctrlsの内部のTWin32WSCustomEditで実装されています。 これは、LCLがインターフェースと対話する方法で、インターフェースが何か仕事をする方法です。
インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(<message_id>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。
新しいWidgetsetを生成する方法
これは、新しいwidgetsetを開発する初歩的なチュートリアルです。 この方法は、新しいqt4インターフェースを作るときの私の経験によるものです。
どうして新しいWidgetsetを追加したくなるのでしょうか。もっと多くのプラットフォームのサポートを追加するために!なぜでしょう。 基本的には、再コンパイルさえすれば、他のいろんなプラットフォームで動作するという、Lazarusのもっとも有利な点を改善するためです。 だから、新しいwidgetsetを追加したいんだ、と言ってみましょう。 まず最初に、widgetのpascalバインディングを取得し、どのようにそれを使うか、知る必要があります。 これは数時間ネット上の基本的なチュートリアルをやってもらえば、それほど難しいことではありません。 それらのチュートリアルをpascalに翻訳すれば、それが導入には充分です。
Qt4を使うとき、私はパスカル向けのDen Jean qt4 bindingsを使いました。そして、とても基本的なプログラムをしました。これがそのプログラムです。
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のプログラムです。これで、Qtプログラムが出来てしまうなんて、素敵ですね。 でも、ちょっとまって下さい。これが本当につくりたかったものではないんです。 私がやりたいのは、すでに存在するアプリケーションを再コンパイルして、Qtとリンクするだけのものですよ ! 私は上のアプリケーションを次のようにします。
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用のLCLのWidgetsetをつくってみます。
最初に新しいwidgetsetでやることは、空っぽの骨組みをつくることです。 初期のqtやcarbonのようなwidgetsetsの開発では、骨組みからはじめます。
多くのwidgetsのファイルを注意深くみると、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からの派生です。 これらのコントロールはHandleなどといったものを持っていて、Widgetsetから作られるべきです。新しいウインドウコンポーネントをwidgetsetに追加するのは簡単です。
それでは、Qt WidgetsetにTQtWSCustomEditを追加してみましょう。
TCustomEditからはじめるために、StdCtrlsユニットのTWinControlの派生にすることです。
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 // Creates the widget WriteLn('Calling QTextDocument_create'); Str := WideString((AWinControl as TCustomMemo).Lines.Text); Widget := QTextEdit_create(@Str, QWidgetH(AWinControl.Parent.Handle)); // Sets it's initial properties 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や他のグラフィカルなオブジェクトを実装することは、コメントがされていないqtwinapi.pasファイルの特別な関数を使っているので、骨が折れる作業かもしれません。
しかし、下のコードをコンパイルしてみてください。
var Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.LoadFromFile('myfile.bmp'); Canvas.Draw(Bitmap, 0, 0); finally Bitmap.Free; end; end;
次のものは、実行時、上のコードでwidgetsetインターフェースがコールされるときに、関数が呼ばれる順序です。
1 - GetDC(0);
デバイスコンテキストを生成するだけです。
2 - GetDeviceRawImageDescription
Qtで内部のピクセルフォーマットを記述するユーティリティです。
3 - CreateBitmapFromRawImage
ここでネイティブなイメージオブジェクトを生成する必要があり、RawDataからロードします。 イメージデータに関する情報は、2でおこなったピクセルフォーマットに基づいて格納されています。
Example of how the interfaces work
Below is a simple example. Supose you have a button component. How would it be implemented for different platforms on the LCL way?
There would be the files:
\trayicon.pas
\wstrayicon.pas
\gtk\gtkwstrayicon.pas
\gtk\trayintf.pas
\win32\win32wstrayicon.pas
\win32\trayintf.pas
This way you require zero ifdefs. You will need to add as a unit path $(LCLWidgetType) for it to add the correct
trayintf.pas file which will in turn initialize the correct WS Tray class.
in trayicon.pas you include wstrayicon. Derive your main class from a LCL class, and only use wstrayicon on the implementation. All LCL classes that communicate with the widget set, are derived from TLCLComponent declared in the LCLClasses unit.
unit TrayIcon; interface type TTrayIcon = class(TLCLComponent) public procedure DoTray; end; implementation uses wstrayicon; procedure TTrayIcon.DoTray; begin // Call wstrayicon end; end.
in trayintf you use gtkwstrayicon or win32trayicon depending on which trayintf file it is.
in wstrayicon you create a class like so:
unit WSTrayIcon; uses WSLCLClasses, Controls, TrayIcon; // and other things as well TWSTrayIcon = class of TWSTrayIcon; TWSTrayIcon = class(TWSWinControl); public class procedure EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual; // these must all be virtual and class procedures!! class procedure RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); virtual; .... end; ... implementation procedure TWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); begin //do nothing end; procedure TWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); begin //do nothing end;
now in gtkwstrayicon.pas do this:
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); // it's something like this anyway TGtkWSWincontrolClass(WidgetSetClass).SetCallbacks(AWinControl); // and more stuff end; function TGtkWSTrayIcon.FindSystemTray(const ATrayIcon: TCustomTrayIcon): TWindow; begin // do something end; procedure TGtkWSTrayIcon.EmbedTrayIcon(const ATrayIcon: TCustomTrayIcon); var SystemTray: TWindow; begin SystemTray := FindSystemTray(ATrayIcon); //do something end; procedure TGtkWSTrayIcon.RemoveTrayIcon(const ATrayIcon: TCustomTrayIcon); begin //do something end; ...... initialization RegisterWSComponent(TCustomTrayIcon, TGtkWSTrayIcon); //this is very important!!! end.
then finally in trayicon.pas you go as normal
uses WSTrayIcon; //etc. you DON'T include GtkWSTrayIcon here! TCustomTrayIcon = class(TWinControl) public procedure EmbedControl; .... end; ... procedure TTrayIcon.EmbedControl; begin TWSTrayIconClass(WidgetSetClass).EmbedControl(Self); end;
This document is work in progress. You can help by writing sections of this document. If you are looking for information in this document but could not find it, please add your question to the discussion page. It will help us to write the documentation that is wanted on a level that is not too simple or too complicated.