Difference between revisions of "LCL Internals/ja"

From Lazarus wiki
Jump to navigationJump to search
Line 24: Line 24:
 
インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(<message_id>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。
 
インターフェースがLCLへコミュニケーションの返答をするのは、ほとんどメッセージ(たいてい、'DeliverMessage')を送ることによってなされます。これは、TControl.Perform(<message_id>, wparam, lparam)で、wparamとlparamに、いろんな情報を入れて送ります。
  
==How to create a new Widgetset==
+
==新しい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.
+
これは、新しいwidgetsetを開発する初歩的なチュートリアルです。
 +
この方法は、新しいqt4インターフェースを作るときの私の経験によるものです。
  
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.
+
どうして新しいWidgetsetを追加したくなるのでしょうか。もっと多くのプラットフォームのサポートを追加するために!なぜでしょう。
 +
基本的には、再コンパイルさえすれば、他のいろんなプラットフォームで動作するという、Lazarusのもっとも有利な点を改善するためです。
 +
だから、新しいwidgetsetを追加したいんだ、と言ってみましょう。
 +
まず最初に、widgetのpascalバインディングを取得し、どのようにそれを使うか、知る必要があります。
 +
これは数時間ネット上の基本的なチュートリアルをやってもらえば、それほど難しいことではありません。
 +
それらのチュートリアルをpascalに翻訳すれば、それが導入には充分です。
  
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.
+
Qt4を使うとき、私はパスカル向けのDen Jean qt4 bindingsを使いました。そして、とても基本的なプログラムをしました。これがそのプログラムです。
  
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:
 
  
 
<pre>
 
<pre>
Line 53: Line 58:
 
</pre>
 
</pre>
  
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:
+
上のプログラムはqt4のプログラムです。これで、Qtプログラムが出来てしまうなんて、素敵ですね。
 +
でも、ちょっとまって下さい。これが本当につくりたかったものではないんです。
 +
私がやりたいのは、すでに存在するアプリケーションを再コンパイルして、Qtとリンクするだけのものですよ
 +
 +
私は上のアプリケーションを次のようにします。
  
 
<pre>
 
<pre>
Line 72: Line 81:
 
</pre>
 
</pre>
  
Being that the form is mainteined by Lazarus IDE and designed visually.
+
こうすることで、フォームが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)

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報

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.