Lazarus Custom Drawn Controls
The Lazarus Custom Drawn Controls are a set of controls equivalent to the standard Lazarus controls, but which draw themselves. They can have many uses, including the ability to fully customize the drawing, the ability to have exactly the same look in different platforms and also a higher consistency of behavior.
This set of controls is divided into two parts:
- the custom drawn controls which are necessary for implementing Lazarus widgetsets and will in future be located in the unit lazarus/lcl/customdrawncontrols.pas. These are described here.
- all other custom drawn controls, which are used often, but aren't indispensable to implement a LCL custom drawn widgetset. Those are located in the package lazarus/components/customdrawn. These are described in the page Lazarus Custom Drawn Package
At the moment all custom drawn controls are in the customdrawn package.
How do these components work?
The basic programming technique utilized by this set of controls is explained in the page Developing with Graphics#Create a custom control which draws itself.
The code of the controls itself is implemented in the unit customdrawncontrols.pas, but this unit has no drawing code at all. Customdrawncontrols.pas has all the code to process all keyboard and mouse events of the control and implements all of it's behavior. Each instance of a control has a Drawer connected to it. A Drawer in our nomenclature (an instance of the TCDDrawer class) is the same thing as a theme rendering engine or similar. The unit customdrawndrawers.pas manages the list of all known drawers and it also declares all basic data types for the State and StateEx, which contain all the information which the control passes to the drawer about the current state of the control so that it can have all required information to draw it.
Only 1 instance of each drawer exists in the program and all controls just refer to it, through the drawers manager in customdrawndrawers.pas. Each control has a property called DrawStyle which allows us to choose from an enumerated type which draw style to use and then customdrawndrawers.pas converts this information into a Drawer instance. One can spacify the dsDefault DrawStyle which will then use the style specified in the global variable DefaultStyle. This was done to allow changing the style of all controls at one by changing this global variable and then calling Invalidate on the controls. Bellow one can see a code snipet from customdrawndrawers.pas which shows the exposed methods to managed the list of drawers and also the Default style:
<delphi> unit customdrawndrawers; //...
TCDDrawStyle = ( // The default is given by the DefaultStyle global variable // Don't implement anything for this drawer dsDefault = 0, // This is a common drawer, with a minimal implementation on which other // drawers base on dsCommon, // Operating system styles dsWinCE, dsWin2000, dsWinXP, dsKDE, dsGNOME, dsMacOSX, dsAndroid, // Other special styles for the user dsExtra1, dsExtra2, dsExtra3, dsExtra4, dsExtra5, dsExtra6, dsExtra7, dsExtra8, dsExtra9, dsExtra10 );
//... procedure RegisterDrawer(ADrawer: TCDDrawer; AStyle: TCDDrawStyle); function GetDefaultDrawer: TCDDrawer; function GetDrawer(AStyle: TCDDrawStyle): TCDDrawer;
var
DefaultStyle: TCDDrawStyle = dsCommon; // For now default to the most complete one, later per platform
</delphi>
Each Drawer class has methods to draw all controls and also all available primitives. All new drawers created by users should inherited from the class TCDCommonDrawer which is the basic common drawer declared in the unit customdrawn_common.pas. If you don't inherit from it, the application might crash if it hits an abstract method. This is the most complete drawer which implements all abstract methods from it's base class, so inheriting form it one guarantees that there will never be crashes due to yet unimplemented methods. Other drawers can override the desired methods to change the drawing style.
The dsCommon drawer uses only TCanvas for all it's rendering, but other themes might also use TLazIntfImage and TFPImageCanvas instead, in order to have a faster pixel access. This programming technique is described in Developing with Graphics#Working with TLazIntfImage.
Color Palette
Drawers have also access to a color Palette, which has the usual system colors from the LCL. In their native platform by default drawers will load their colors from the operating system via the LCL, but outside their native platform drawers will use a standard color palette.
<delphi>
TCDColorPalette = class public ScrollBar, Background, ActiveCaption, InactiveCaption, Menu, Window, WindowFrame, MenuText, WindowText, CaptionText, ActiveBorder, InactiveBorder, AppWorkspace, Highlight, HighlightText, BtnFace, BtnShadow, GrayText, BtnText, InactiveCaptionText, BtnHighlight, color3DDkShadow, color3DLight, InfoText, InfoBk, // HotLight, GradientActiveCaption, GradientInactiveCaption, MenuHighlight, MenuBar, Form: TColor; end;
{ There are 5 possible sources of input for color palettes: palDefault - Uses palNative when the operating system matches the drawer style, palFallback otherwise palNative - Obtain from the operating system palFallback - Use the fallback colors of the drawer palUserConfig-Load it from the user configuration files, ToDo palCustom - The user application has set its own palette } TCDPaletteKind = (palDefault, palNative, palFallback, palUserConfig, palCustom);
{ TCDDrawer }
TCDDrawer = class protected public Palette: TCDColorPalette; constructor Create; virtual; destructor Destroy; override; procedure SetPaletteKind(APaletteKind: TCDPaletteKind); procedure LoadNativePaletteColors; procedure LoadFallbackPaletteColors; virtual;
</delphi>
As the code above shows, one can choose where the colors should come from: From the operating system current theme, from the default of the drawer or defined by the user application. This allows one to change all colors of an application at run time by just changing the palette.
The Drawers
dsCommon
This is the base drawer for all others and it implements a Win2000-look with some small improvements, like better focus drawings on controls
dsWinCE
Immitates the look of Windows CE, but with some small improvements like better focus drawings on controls
dsWin2000
Immitates the look of Windows 2000, because dsCommon already does the same, this class implements no methods at all
dsWinXP
Immitates the look of Windows XP
dsKDEPlastique
Immitates the look of the Plastique Qt/KDE Theme
dsAndroid
Immitates the look of Android
Controls which immitate the Standard Palette
TCDButton
This is a fully custom drawn button.
Usage example:
<delphi> uses customdrawnextras;
procedure TForm1.FormCreate(Sender: TObject); var
MyButton: TCDButton;
begin
MyButton := TCDButton.Create(Self); MyButton.Parent := Self; MyButton.DrawStyle := dsWin2000; MyButton.Left := 100; MyButton.Top := 100; MyButton.Width := 200; MyButton.Height := 50; MyButton.Caption := 'My Button'; MyButton.Color := clRed; MyButton.OnClick := @HandleButtonClick;
end; </delphi>
TCDEdit
This is a fully custom drawn edit control.
TCDCheckBox
This is a fully custom drawn check box.
TCDScrollBar
TCDGroupBox
This is a fully custom drawn group box.
Controls which immitate the Additional Palette
TCDStaticText
Controls which immitate the Common Controls Palette
TCDTrackBar
Substitutes TTrackBar
TCDProgressBar
TCDListView
Under construction.
TCDTabControl
Substitutes TTabControl
TCDPageControl
Substitutes TPageControl
Custom Drawn Packages
Moved here: Lazarus Custom Drawn Package
Other good custom drawn components for Lazarus
Maintainers
- Felipe Monteiro de Carvalho
- JiXian Yang