Difference between revisions of "LCL Internals"
Sekelsenmat (talk | contribs) (thanks for Andrew Haines on explaining this. The example needs some work to be more clear.) |
Sekelsenmat (talk | contribs) |
||
Line 33: | Line 33: | ||
trayintf.pas file which will in turn intialize the correct WS Tray class. | trayintf.pas file which will in turn intialize the correct WS Tray class. | ||
− | |||
in trayicon.pas you include wstrayicon and trayintf | in trayicon.pas you include wstrayicon and trayintf | ||
Line 41: | Line 40: | ||
then in wstrayicon you create a class like so: | then in wstrayicon you create a class like so: | ||
+ | <pre> | ||
unit WSTrayIcon; | unit WSTrayIcon; | ||
Revision as of 21:48, 22 December 2005
Internals of the LCL
There is the LCL, and the "interface". The LCL is the part that is platform independent, and it resides in the lazarus/lcl/ directory. This directory contains mainly class definitions. Many of the different controls are actually implemented in the lazarus/lcl/include/ directory in the various .inc files. This is to find the implementation of a specific control, TCustomMemo for example, faster (which is in custommemo.inc). Every .inc starts with a line {%MainUnit ...} to define where it is included.
Then there is the "interface" which lives in a subdirectory of the lazarus/lcl/interfaces/ directory. The gtk interface is in gtk/, win32 in win32/, etc. They all have a Interfaces unit, which is used by the lcl and creates the main interface object. Usually the main interface object is defined in XXint.pp (win32int.pp), and implemented in various inc files, XXobject.inc, for the interface specific methods, XXwinapi.inc for winapi implementation methods, XXlistsl.inc for implementation of the stringlist used by the TComboBox, TListBox, and other such controls, XXcallback.inc for handling of widget events and taking appropriate action to notify the LCL.
Every control has a WidgetSetClass property which is of the 'mirror' class in the interfaces directory, for example: mirror of TCustomEdit is TWSCustomEdit, which methods are implemented by TWin32WSCustomEdit in win32wsstdctrls. This is the way the LCL communicates with the interface, and how it lets the interface do things.
Communication of interface back to LCL is mostly done by sending messages, usually 'DeliverMessage' which calls TControl.Perform(<message_id>, wparam, lparam) with wparam and lparam being the extra info for the message.
Example of how the interfaces work
Below is a simple example. Supose you have a trayicon 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.
so you add as a unit path: $(LCLWidgetType) and it will use the correct trayintf.pas file which will in turn intialize the correct WS Tray class.
in trayicon.pas you include wstrayicon and trayintf
in trayintf you use gtkwstrayicon or win32trayicon depending on which trayintf file it is.
then 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.