Developing and Installing a Component

This section is strictly for enthusiasts. There are many steps, but it is very satisfying when you can drop your own component onto a form and change its properties using the Object Inspector.

The following steps describe how to create a component, develop it and install it in Lazarus. These steps make it easy to include your own icon for display in one of the component tabs; the icon is automatically converted to a resource file and a reference to it included in the automatically generated source file. This new visual component is very loosely based on a roulette wheel, hence its name.

  1. Prepare and your own 24 x 24 icon such as in a graphics application such as Paint and save it (as a .png file, for example).
  2. Create a new application to test the new component and save it in a safe place. We created a folder C:\Users\PPS\AppData\Local\lazarus\NewComponent, selected menu item File > Save All, saved the project as NewComponentTest and the unit as uNewComponentTest.
  3. Select menu item Package > New Package and save it as TRSWheel.
  4. Click on the Add icon in the Package dialogue to see the Add to package dialogue then select the New Component tab.
  5. Click the Icon button then navigate to your image and open it. Complete the form as in this screenshot:
    New Component

    New Component

  6. Click the Create New Component button.
    You should now see the code of the new unit RSWheel.
  7. Add code to the new unit.
    The code we used was based on the Lazarus shapes code:
    unit RSWheel;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Types;
    
    type
      TRSWheel = class(TGraphicControl)
       private
        FPen: TPen;
        FBrush1, FBrush2: TBrush;
        FSegments:integer;
        procedure SetFSegments(Value: integer );
        procedure SetBrush1(Value: TBrush);
        procedure SetBrush2(Value: TBrush);
        procedure SetPen(Value: TPen);
        class function GetControlClassDefaultSize: TSize; override;
      protected
        procedure Paint; override;
      public
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
      published
        procedure StyleChanged(Sender: TObject);
        property Align;
        property Anchors;
        property Brush1: TBrush read FBrush1 write SetBrush1;
        property Brush2: TBrush read FBrush2 write SetBrush2;
        property Segments: Integer read FSegments write SetFSegments default 14;
        property DragCursor;
        property DragKind;
        property DragMode;
        property Enabled;
        property Constraints;
        property ParentShowHint;
        property Pen: TPen read FPen write SetPen;
        property ShowHint;
        property Visible;
        property OnContextPopup;
        property OnDragDrop;
        property OnDragOver;
        property OnEndDock;
        property OnEndDrag;
        property OnMouseDown;
        property OnMouseMove;
        property OnMouseUp;
        property OnStartDock;
        property OnStartDrag;
      end;
    
    procedure Register;
    
    implementation
    
    constructor TRSWheel.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      with GetControlClassDefaultSize do
        SetInitialBounds(0, 0, CX, CY);
      ControlStyle := ControlStyle + [csReplicatable];
      FPen := TPen.Create;
      FPen.OnChange := @StyleChanged;
      FBrush1 := TBrush.Create;
      FBrush2 := TBrush.Create;
      FBrush1.OnChange := @StyleChanged;
      FBrush2.OnChange := @StyleChanged;
      FBrush1.Color:=clGreen;
    end;
    
    destructor TRSWheel.Destroy;
    begin
      FPen.Free;
      FBrush1.Free;
      FBrush2.Free;
      inherited Destroy;
    end;
    
    procedure TRSWheel.Paint;
    var
      X, Y, W, H, S, k, relX, relY, fillX, fillY: integer;
      deltaX, deltaY, radius, angle, XfromCentre, YfromCentre: real;
    begin
      with Canvas do
        if (Self.Width >= 250) and (Self.Height >= 250) then
          begin
            Pen := FPen;
            Brush := FBrush1;
            X := Pen.Width div 2;
            Y := X;
            W := Self.Width - Pen.Width + 1;
            H := Self.Height - Pen.Width + 1;
            if Pen.Width = 0 then
              begin
                dec(W);
                dec(H);
              end;
            //Size S to be the length of the smaller dimension
            if W < H then
              S := W
            else
              S := H;
            inc(X, (W - S) div 2);
            inc(Y, (H - S) div 2);
            W := S;
            H := S;
            //Draw circle
            ellipse(X, Y, X + W, Y + H);
          end; //of with canvas
      if FSegments = 0  then
        SetFSegments(30);
      //Draw segments
      radius := S / 2;
      for k := 0 to Segments - 1 do
        begin
          angle := 2 * k * pi / Segments;
          deltaX := radius * sin(angle);
          deltaY := radius * (1 - cos(angle));
          relX := trunc(radius + deltaX);
          relY := trunc(deltaY);
          Canvas.MoveTo(X + trunc(radius), Y + trunc(radius));
          Canvas.LineTo(X + relX, Y + relY);
        end;
      // Fill segments
      for k := 0 to Segments - 1 do
        begin
          if k mod 2 = 0 then
            canvas.Brush := FBrush1
          else
            canvas.Brush := FBrush2;
          angle := (2 * k + 1) * (pi / FSegments); //radians
          XfromCentre := radius * 0.9 * sin(angle);
          YfromCentre := radius * 0.9 * cos(angle);
          fillX := trunc(radius + XfromCentre);
          fillY := trunc(radius - YfromCentre);
          Canvas.FloodFill(X + fillX, Y + fillY, clBlack, fsBorder);
          Canvas.TextOut(X + fillX - 5, Y + fillY - 5, inttostr(k + 1));
        end; //for
    end;
    
    procedure TRSWheel.StyleChanged(Sender: TObject);
    begin
      Invalidate;
    end;
    
    procedure TRSWheel.SetBrush1(Value: TBrush);
    begin
      FBrush1.Assign(Value);
    end;
    
    procedure TRSWheel.SetBrush2(Value: TBrush);
    begin
      FBrush2.Assign(Value);
    end;
    
    procedure TRSWheel.SetPen(Value: TPen);
    begin
      FPen.Assign(Value);
    end;
    
    procedure TRSWheel.SetFSegments(Value: integer);
    begin  //Number of segments must be even and between 6 and 32 inclusive
      if (FSegments <> Value) and (Value >= 6) and (Value <= 32) and (Value Mod 2 = 0) then
        begin
          FSegments := Value;
          Invalidate;
        end;
    end;
    
    class function TRSWheel.GetControlClassDefaultSize: TSize;
    begin
      Result.CX := 250;
      Result.CY := 250;
    end;
    
    
    procedure Register;
    begin
      {$I rswheel_icon.lrs}
      RegisterComponents('Additional',[TRSWheel]);
    end;
    
    end.
    
    
  8. Select menu item Package > Open Recent Package, select your new package, and click on the Compile icon.
    You should see a message that the compiling has completed.
  9. Select menu item Use >> > Install and accept the invitation to continue.
    Lazarus will probably disappear then reappear. Our Additional components tab now looks like this:
    Additional Component Tab

    Additional Component Tab

  10. Select your test unit in the source editor, press F12 to see the design view of your empty form and drop the new component onto it.
  11. Adjust the size of the form and centre the component in it:
    Design view of new component

    Design view of new component

  12. Change some properties in the Object Inspector and compile the test application.
    We changed the number of segments and both brush colours:
    Output of new component

    Output of new component

Programming - a skill for life!

Using widgets (such as list boxes, combo boxes, string grids, DBgrids, charts and maps) and drawing on the canvas