Difference between revisions of "LCL Internals/ja"
m |
|||
Line 3: | Line 3: | ||
{{Japanese Menu}} | {{Japanese Menu}} | ||
− | == | + | == LCLの内部 == |
LCLとその「インタフェース」があります。 LCLはプラットホーム独立を担う部分です。そして、それはlazarus/lcl/ディレクトリにあります。 このディレクトリは主要なクラス定義を含んでいます。 | LCLとその「インタフェース」があります。 LCLはプラットホーム独立を担う部分です。そして、それはlazarus/lcl/ディレクトリにあります。 このディレクトリは主要なクラス定義を含んでいます。 | ||
多くのさまざまなコントロールが、様々な.incファイルの中のlazarus/lcl/include/ディレクトリに実装されています。これは、より速く特定のコントロールの実装を見つけるためです。TCustomMemoを例にとると、それは、custommemo.incにあります。 | 多くのさまざまなコントロールが、様々な.incファイルの中のlazarus/lcl/include/ディレクトリに実装されています。これは、より速く特定のコントロールの実装を見つけるためです。TCustomMemoを例にとると、それは、custommemo.incにあります。 | ||
どの.incファイルも、{%MainUnit...}ではじまり、どこでそれがインクルードされているかを定義しています。 | どの.incファイルも、{%MainUnit...}ではじまり、どこでそれがインクルードされているかを定義しています。 | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
そして、lazarus/lcl/interfaces/内のサブディレクトリに、「インターフェース」があります。 | そして、lazarus/lcl/interfaces/内のサブディレクトリに、「インターフェース」があります。 | ||
Line 27: | Line 18: | ||
XXcallback.incはウイジェットイベントのハンドリングと、適切にLCLへ通知をおこなうものです。 | XXcallback.incはウイジェットイベントのハンドリングと、適切にLCLへ通知をおこなうものです。 | ||
+ | すべてのコントロールは、WidgetSetClassプロパティをもっています。これは、インターフェースディレクトリのミラーのクラスです。たとえば、TCustomEditのミラーは、TWSCustomEditです。 | ||
+ | このメソッドは、win32wsstdctrlsの内部のTWin32WSCustomEditで実装されています。 | ||
+ | これは、LCLがインターフェースと対話する方法で、インターフェースが何か仕事をする方法です。 | ||
− | + | インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(<message_id>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==How to create a new Widgetset== | ==How to create a new Widgetset== |
Revision as of 08:22, 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に、いろんな情報を入れて送ります。
How to create a new Widgetset
This is a step-by-step tutorial of developing a new widgetset. It is based on my experience creating the basics of the new qt4 interface.
Why would someone want to add an Widgetset? To add support for more platforms! And why? Basically to take improve the greatest advantage of Lazarus: Just recompile your already working code for various different platforms.
So, let's say you want to add a new widgetset! First of all, you need to be able to have pascal bindings for the widget and know how to use it. It is not hard, a few hours doing basic tutorials available on the net, just translating them to pascal should be enougth to get started.
Now, for Qt4 I used Den Jean qt4 bindings for pascal, and created a very basic Qt program using them. Here is the code of that program:
program qttest; uses qt4; var App: QApplicationH; MainWindow: QMainWindowH; begin App := QApplication_Create(@argc,argv); MainWindow := QMainWindow_Create; QWidget_show(MainWindow); QApplication_Exec; end.
The abovo project compiles and creates a qt4 program. Really nice hum! Now I can create Qt programs!!! But wait .... that's not what I really want. What I really want is to recompile by existing applications and see them link only to Qt! I would like the application above to look like to one below and compile fine:
program qttest; {$mode objfpc}{$H+} uses Interfaces, Classes, Forms, { Add your units here } qtform; begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
Being that the form is mainteined by Lazarus IDE and designed visually.
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.