md
First Lazarus Components - Part 1

Next: Part 2
Original: 2016-01-24

As mentioned, I have moved from Delphi to Lazarus. And at the same time, I have changed from Windows to Linux. The change has not been without problems, but I think it will work out in the end.

While Lazarus is quite similar to older versions of Delphi, cross-platform development does impose some constraints. To learn the details, I have decided to implement some software originally developed with Delphi on Windows in this new (for me) environment. And I am making some progress: conversion of the mathematical parser is complete while Caleçon, a calculator, is for the most part complete. And I am starting on a complete rewrite of Gaston, a function grapher.

It may prove useful to have a colour selection dialogue much like the ones found in Inkscape or Gimp. That gave me an excuse to create my first components: TColorBar and TSliderBar. They can be seen in the following screen shot of the second demonstration program that accompanies this topic. The red, green and blue bars are of type TColorBar while the other three shapes are of type TSliderBar .

demo 2 image
Figure 1: Demo 2, Using colour bars to define a colour

There are three demonstration programs included in the dowloadable archive. The first can be run without installing the components. It will give you an idea of capabilities of the TColorBar component. It is basically a colour track bar which can be associated with a TSpinEdit for finer control.

demo 1 image
Figure 2: Demo 1, Colour bar properties

The TSliderBar component can be thought of as an owner drawn TColorBar. As such, it is relatively easy to create a track bar with that component. See Demo3 in the archive. The components does not need to be be installed in the Lazarus IDE to compile this example.

You must install the components to compile the second demonstration program. This entails installing a package file. It is named MdSliderBarsLaz.lpk and is located in the package folder. Follow the usual steps to install the package, which means accepting the default values at each step.

To create the colour selector, seen in the top image, drop three TColorBars and three spin edits on a form. Add a TSliderBar , the labels and a TButtonPanel. Using the object inspector, adjust the displayed buttons and displayed glyphs on the buttons of the TButtonPanel to your taste.

Again, with the object inspector, change the following properties in each of the colour bar:

  1. Associate: set to the appropriate spin edit
  2. MarkerStyle: set to msTriangular (optional as long as msNone is not chosen)
  3. MaxValue: 255 (again optional but the natural range 0-255 is what Inkscape uses)
Set the OnChange event of the three bars to a common event handler which could be called ColorBarChange. That handler is a simple calls to a routine, RedrawBars, that redraws the appropriate components whenever a colour value is changed either by using the mouse or keyboard arrows to move the slider in a colour bar or by entering a value in the associated spin edit. The colour bar handles its associated spin edit, there is nothing to do with it.

As for the TSliderBar change the following property

  1. MarkerStyle: set to msNone
  2. TabStop: set to False.

Click on the component's OnPaint event to create a handler which will fill the left half with the initial colour and the right half with the currenly defined colour.

procedure TPickerForm.colorBarPaint(Sender: TObject; aRect: TRect);
var
  middle: integer;
  orgRight: integer;
begin
  orgRight := aRect.Right;
  middle := (aRect.left + orgRight) div 2;
  with Sender as TSliderBar do begin
    with canvas do begin
      // fill the left half with the initial colour
      aRect.right := middle;
      brush.color := InitialColor;
      FillRect(aRect);
      // and fill the right half with the currently selected colour
      aRect.Right := orgRight;
      aRect.Left := middle;
      brush.color := SelectedColor;
      FillRect(aRect);
    end;
  end;
end;

Now there remains the task of writing the code, which is very simple because most of the work is done by the components. Start by adding the private RedrawBars procedure and by defining two private variables of the same type representing the initial colour when starting the dialogue and the selected colour which will be updated as the user changes a colour value.

type
  TPickerForm = class(TForm)
    ...
  private
    { private declarations }
    FInitial, FSelected: packed record
      case boolean of
        false: (color: TColor);
        true: (Red: byte;
               Green: byte;
               Blue: byte;
               Alpha: byte);
      end;
    procedure RedrawBars;
  public
    { public declarations }
    property InitialColor: TColor read FInitial.Color write FInitial.Color;
    property SelectedColor: TColor read FSelected.color;
  end;

These two variables are exposed as public properties called InitialColor which is read and write and SelectedColor which is read only. Note how the record gives access to the colours as both TColor and as individual RGB values (the Alpha value is not used here).

The procedure RedrawBars does most of the work. First, it updates the selected colour stored in the FSelected record. Then each of the three colour bars is redrawn. Each colour bar is a linear gradients along the corresponding colour channel. Take the red bar as an example. The gradient will be drawn from a red value of 0 to the maximum red value of 255. The values of green and blue channel are constant and equal to the values of the selected colour. While the start colour and end colour are properties which can be set individually, it is better to use the SetColors procedure to avoid unnecessary drawing:

  with FSelected do 
    RedBar.SetColors(RGB(0, Green, Blue), RGB(255, Green, Blue));

The other colour bars are updated similarly. Here is the complete code for the procedure:

procedure TPickerForm.RedrawBars;
begin
  with FSelected do begin
    // update the selected colour using the colour bar values
    // one of which has presumably been changed
    Red := RedBar.value;
    Green := GreenBar.value;
    Blue := BlueBar.value;
    // redraw all the colour bars, because they are interdependant
    RedBar.SetColors(RGB(0, Green, Blue), RGB(255, Green, Blue));
    GreenBar.SetColors(RGB(Red, 0, Blue), RGB(Red, 255, Blue));
    BlueBar.SetColors(RGB(Red, Green, 0), RGB(Red, Green, 255));
    // update the hexadecima value of the selected color
    label4.Caption := Format('%.2x%.2x%.2x', [Red, Green, Blue]);
  end;
  // redraw the control showing the selected color
  colorBar.Invalidate;
end;

The only other significant procedure paints the initial and selected colour bar at the bottom. TSliderBar is a TCustomControl descendant, so that its Canvas property is public. The client area is specified in a TRect variable. Drawing the inside of the bar is a simple matter of filling the left half with the original colour and the right half with the selected colour.

Upcoming: addition of a two-dimensional slider component (see Part 2).

Test environment: Lazarus 1.4.4, FPC 2.64, Ubuntu 14.04 LTS 64 bit.

Modifications: as of the 26 th of January, the following corrections have been made to the the package:

  1. Addition of a 3rd demonstration program.
  2. Unhooked old associate’s OnChange event in TCustomSlider.SetAssociate.
  3. Added a destructor to TCustomSlider to unhooked associate’s OnChange event.
  4. Fixed the default value for MarkerStyle.
  5. Fixed the package file (small Lazarus/Delphi gotcha).

Download the package archive: mdsliderbarslaz.zip. See Part 2 for a bigger package containing an additional two components.