Difference between revisions of "LCL Internals/ja"
Line 24: | Line 24: | ||
インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(<message_id>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。 | インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(<message_id>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。 | ||
− | == | + | ==新しいWidgetsetを生成する方法== |
− | + | これは、新しいwidgetsetを開発する初歩的なチュートリアルです。 | |
+ | この方法は、新しいqt4インターフェースを作るときの私の経験によるものです。 | ||
− | + | どうして新しいWidgetsetを追加したくなるのでしょうか。もっと多くのプラットフォームのサポートを追加するために!なぜでしょう。 | |
+ | 基本的には、再コンパイルさえすれば、他のいろんなプラットフォームで動作するという、Lazarusのもっとも有利な点を改善するためです。 | ||
+ | だから、新しいwidgetsetを追加したいんだ、と言ってみましょう。 | ||
+ | まず最初に、widgetのpascalバインディングを取得し、どのようにそれを使うか、知る必要があります。 | ||
+ | これは数時間ネット上の基本的なチュートリアルをやってもらえば、それほど難しいことではありません。 | ||
+ | それらのチュートリアルをpascalに翻訳すれば、それが導入には充分です。 | ||
− | + | Qt4を使うとき、私はパスカル向けのDen Jean qt4 bindingsを使いました。そして、とても基本的なプログラムをしました。これがそのプログラムです。 | |
− | |||
<pre> | <pre> | ||
Line 53: | Line 58: | ||
</pre> | </pre> | ||
− | + | 上のプログラムはqt4のプログラムです。これで、Qtプログラムが出来てしまうなんて、素敵ですね。 | |
+ | でも、ちょっとまって下さい。これが本当につくりたかったものではないんです。 | ||
+ | 私がやりたいのは、すでに存在するアプリケーションを再コンパイルして、Qtとリンクするだけのものですよ | ||
+ | ! | ||
+ | 私は上のアプリケーションを次のようにします。 | ||
<pre> | <pre> | ||
Line 72: | Line 81: | ||
</pre> | </pre> | ||
− | + | こうすることで、フォームがLazarus IDEでメンテナンスできて、ビジュアルな設計が可能になります。 | |
So now I must create a LCL Widgetset for Qt. | So now I must create a LCL Widgetset for Qt. |
Revision as of 08:37, 13 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でメンテナンスできて、ビジュアルな設計が可能になります。
So now I must create a LCL Widgetset for Qt.
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.
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:
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;
How to implement a new windowed component
Windowed components are all descendents from TWinControl. Those controles have a Handle and thus, should be created by the Widgetset. It's easy to add new windowed components to a widgetset.
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.
Now, go to QtWSStrCtrls unit and look for the declaration of TQtWSCustomEdit.
TQtWSCustomEdit = class(TWSCustomEdit) private protected public end;
Add static methods that are declared on TWSCustomEdit and override them. The code should now look like this:
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;
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 enought for it to be show on the form and be editable, so it fits our needs on this article.
Hit CTRL+SHIFT+C to code complete and the implement CreateHandle and DestroyHandle. In the case of Qt4 the code will be like this:
{ 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;
Now uncomment the like "RegisterWSComponent(TCustomEdit, TQtWSCustomEdit);" on the bottom of the unit and that's it!
You can now drop a TCustomEdit on the bottom of a form and expect it to work. :^)
Implementing TBitmap
Implementing TBitmap and other graphical objects can be hard, because they use some special function on qtwinapi.pas file and there are no comments on those functions.
So, let's say you want to compile the following code:
var Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.LoadFromFile('myfile.bmp'); Canvas.Draw(Bitmap, 0, 0); finally Bitmap.Free; end; end;
Bellow is the order on which functions from the widgetset interface are called when executing that code:
1 - GetDC(0);
Just create a device context.
2 - GetDeviceRawImageDescription
Describe the inner pixel format utilized by Qt
3 - CreateBitmapFromRawImage
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.
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.