Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I write a Visual Delphi Component?

Component Writing

How do I write a Visual Delphi Component?

by  djjd47130  Posted    (Edited  )
This project is an old one of mine which took a lot of work, but I finally got a good chunk of it working well. It's still far from finished, but the most important parts work. It's a needle gauge component featuring unlimited needles, each of which can have its own set of unlimited tick mark sets. The needles and tick marks are implemented through the use of TCollection's. Each needle can be colored and sized, as well as each set of tick marks. I've already built an analog clock application with it, but that would be too much code to post here.

Here's a picture of how it is now:
[img http://i1026.photobucket.com/albums/y328/djjd47130/Misc/JDNeedleGaugeV12-1.png]

And here's a picture of how I want it to look like in the future, same picture which sparked the whole idea in the first place:
[img http://i1026.photobucket.com/albums/y328/djjd47130/Misc/BlackBlueGauge.jpg]

So, finally, the source code:

Code:
{
  Custom Needle Gauge Component
  by Jerry Dodge
  Component: TJDNeedleGauge
  Sub-Classes:
  - TNeedles: TCollection - Group of needle objects
  - TNeedle: TCollectionItem - Individual needle object
  - TTickMarks: TCollection - Group of tick mark objects for one needle
  - TTickMark: TCollectionItem - Individual set of tick marks for one needle
  - THotspots: TPersistent - Group of possible TPoint's for clickability
      (Used for ability to click Needles or Tick Marks - need to redesign)
  Abilities:
  - Add virtually unlimited number of needles to point around a canvas
  - Add virtually unlimited number of tick mark sets, unique for each needle
  - Fade background with a custom color
  - Color and size each needle and tick mark
  Coming Soon:
  - Set a needle's range around a certain start/stop point around canvas
      (Partially working, needs a lot of development)
  - Use images for Needles and Tick Marks instead of plain Lines and Elipses
      (Picture properties exist, need to implement drawing)
  - Add centerpiece elipse or image over needles
      (Some properties exist, but is far from working)
  - Ability to click on Needles and Tick Marks
      (Partially working, needs a lot of development)
      - Changing cursor over Needles or Tick Marks is somewhat working
      - Mouse events distinctly over Needles or Tick Marks is somewhat working
          (Need to recognize the exact Needle or Tick Mark in event)
      - Functionality wrapped in "THotspots" class but needs to be redesigned
  - Position a needle anywhere on the canvas, not just the center point
      (No development started yet for this)
  - Smooth drawing of lines and elipses on canvas
      (No development started yet for this)
  To Do:
  - Properly implement degree start/stop of needles and tick marks
  - Properly implement percentage length of needles
  - Implement drawing pictures for needles and tick marks
}

unit JDNeedleGauge;

interface
         
uses
  Classes, Controls, SysUtils, Windows, StdCtrls, ExtCtrls, Graphics, StrUtils,
  JPEG, Messages, Variants, Forms, ComCtrls;

type
  TJDNeedleGauge = class;
  TNeedles = class;
  TNeedle = class;
  TTickMarks = class;
  TTickMark = class;
  THotspots = class;

  TPoints = array of TPoint;
        
  TJDMouseEvent = procedure(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer) of object;
  TJDMouseMoveEvent = procedure(Sender: TObject; Shift: TShiftState;
    X, Y: Integer) of object;

  TJDGTickStyle = (gtsNone, gtsCircle, gtsBall, gtsLine);
  TJDGNeedleStyle = (gnsLine, gnsImage);
  TJDGNeedleSizing = (gnsAuto, gnsPixels, gnsPercent);

  THotspot = record
    Point: TPoint;
    Size: Integer;
    Active: Bool;
    Value: String;
  end;

  THotspots = class(TPersistent)
  private
    fHotspots: String;
    function GetHotspot(Point: TPoint): THotspot;
    procedure SetHotspot(Point: TPoint; Hotspot: THotspot);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    function InHotspot(APoint: TPoint): THotspot;
    property Hotspot[Point: TPoint]: THotspot read GetHotspot write SetHotspot;
  end;

  TTickMarks = class(TCollection)
  private
    fOwner: TJDNeedleGauge;
    fEvent: TNotifyEvent;
    fRedraw: TNotifyEvent;
    function GetItems(Index: Integer): TTickMark;
    procedure SetItems(Index: Integer; Value: TTickMark);
    procedure GaugeEvent(Sender: TObject);
    procedure GaugeRedraw(Sender: TObject);
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TPersistent);
    function Add: TTickMark;
    property OnEvent: TNotifyEvent read fEvent write fEvent;
    property OnRedraw: TNotifyEvent read fRedraw write fRedraw;
    property Items[Index: Integer]: TTickMark read GetItems write SetItems;
  end;

  TTickMark = class(TCollectionItem)
  private
    fHotspots: THotspots;
    fOwner: TJDNeedleGauge;
    fIncrement: Integer;
    fSize: Integer;
    fColor: TColor;
    fCanClick: Bool;
    fVisible: Bool;
    fEvent: TNotifyEvent;
    fRedraw: TNotifyEvent;
    procedure DoEvent;
    procedure DoRedraw;
    procedure SetIncrement(Value: Integer);
    procedure SetSize(Value: Integer);
    procedure SetColor(Value: TColor);
    procedure SetCanClick(Value: Bool);
    procedure SetVisible(Value: Bool); 
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(AOwner: TCollection); override;
    destructor Destroy; override;    
    procedure Assign(source:TPersistent); override;
    property OnEvent: TNotifyEvent read fEvent write fEvent;
    property OnRedraw: TNotifyEvent read fRedraw write fRedraw;
  published
    property Increment: Integer read fIncrement write SetIncrement;
    property Size: Integer read fSize write SetSize;
    property Color: TColor read fColor write SetColor;
    property CanClick: Bool read fCanClick write SetCanClick;
    property Visible: Bool read fVisible write SetVisible;
  end;

  TNeedles = class(TCollection)
  private
    fOwner: TComponent;
    fEvent: TNotifyEvent;
    fRedraw: TNotifyEvent;
    procedure NeedleEvent(Sender: TObject);
    procedure NeedleRedraw(Sender: TObject);
  protected
    function GetOwner: TPersistent; override;
    function GetItem(Index: Integer): TNeedle;
    procedure SetItem(Index: Integer; Value: TNeedle);
    procedure Update(Item: TNeedle);
  public
    constructor Create(AOwner: TComponent);
    function Add: TNeedle;
    function Insert(Index: Integer): TNeedle;
    property OnEvent: TNotifyEvent read fEvent write fEvent;
    property OnRedraw: TNotifyEvent read fRedraw write fRedraw;
    property Items[Index: Integer]: TNeedle read GetItem write SetItem; default;
  end;

  TNeedle = class(TCollectionItem)
  private                  
    fPicture: TPicture; 
    fHotspots: THotspots;
    fTickMarks: TTickMarks;
    fOwner: TPersistent;
    fLength: Integer;
    fWidth: Integer;
    fColor: TColor;
    fTransparentColor: TColor;
    fStyle: TJDGNeedleStyle;
    fCursor: TCursor;
    fPosition: Integer;
    fCanClick: Bool;
    fMin: Integer;
    fMax: Integer;
    fDegreeStart: Integer;
    fDegreeStop: Integer;
    fSizing: TJDGNeedleSizing;
    fPassMax: Bool;
    fVisible: Bool;
    fEvent: TNotifyEvent;
    fRedraw: TNotifyEvent;
    procedure DoEvent;
    procedure DoRedraw;
    procedure SetPicture(Value: TPicture);
    procedure SetLength(Value: Integer);
    procedure SetWidth(Value: Integer);
    procedure SetColor(Value: TColor);
    procedure SetTransparentColor(Value: TColor);
    procedure SetStyle(Value: TJDGNeedleStyle);
    procedure SetCursor(Value: TCursor);   
    procedure SetPosition(Value: Integer);
    procedure SetMin(Value: Integer);
    procedure SetMax(Value: Integer);
    procedure SetDegreeStart(Value: Integer);
    procedure SetDegreeStop(Value: Integer);
    procedure SetTickMarks(Value: TTickMarks);
    procedure SetSizing(Value: TJDGNeedleSizing);
    procedure SetPassMax(Value: Bool);
    procedure SetVisible(Value: Bool);
    procedure SetIndex(Value: Integer); override;
    procedure TickEvent(Sender: TObject);
    procedure TickRedraw(Sender: TObject);
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(AOwner: TCollection); override;
    destructor Destroy; override;
    property OnEvent: TNotifyEvent read fEvent write fEvent;
    property OnRedraw: TNotifyEvent read fRedraw write fRedraw;
  published
    property Picture: TPicture read fPicture write SetPicture;
    property Length: Integer read fLength write SetLength;
    property Width: Integer read fWidth write SetWidth;
    property Color: TColor read fColor write SetColor;
    property TransparentColor: TColor read fTransparentColor
      write SetTransparentColor;
    property Style: TJDGNeedleStyle read fStyle write SetStyle;
    property Cursor: TCursor read fCursor write SetCursor;
    property Position: Integer read fPosition write SetPosition;
    property CanClick: Bool read fCanClick write fCanClick;
    property Min: Integer read fMin write SetMin;
    property Max: Integer read fMax write SetMax;
    property DegreeStart: Integer read fDegreeStart write SetDegreeStart;
    property DegreeStop: Integer read fDegreeStop write SetDegreeStop;
    property TickMarks: TTickMarks read fTickMarks write SetTickMarks;
    property Sizing: TJDGNeedleSizing read fSizing write SetSizing;
    property PassMax: Bool read fPassMax write SetPassMax;
    property Visible: Bool read fVisible write SetVisible;
  end;     

  TJDNeedleGauge = class(TCustomControl)
  private
    fBmp: TBitmap;
    fBackground: TBitmap;
    fNeedles: TNeedles;
    fPicture: TPicture;
    fBackColor: TColor;
    fForeColor: TColor;
    fLabelTop: String;
    fLabelBottom: String;
    fDoFade: Bool;
    fFadeStep: Single;
    fShowCenter: Bool;
    fCenterColor: TColor;
    fCenterSize: Integer;
    fMouseDown: TJDMouseEvent;
    fMouseUp: TJDMouseEvent;
    fMouseMove: TJDMouseMoveEvent;
    fNeedleMouseDown: TJDMouseEvent;
    fNeedleMouseUp: TJDMouseEvent;
    fNeedleMouseMove: TJDMouseMoveEvent;
    procedure CreateBackground;
    procedure DrawScale;
    procedure DrawNeedles;
    procedure DrawNeedle(ANeedle: TNeedle);
    procedure DrawCenter;
    procedure DisplayGauge;
    procedure SetBackColor(Value: TColor);
    procedure SetForeColor(Value: TColor);
    procedure SetDoFade(Value: Bool);
    procedure SetShowCenter(Value: Bool);
    procedure SetCenterColor(Value: TColor);
    procedure SetCenterSize(Value: Integer);
    procedure SetLabelTop(Value: String);
    procedure SetLabelBottom(Value: String);
    procedure SetFadeStep(Value: Single);
    procedure OnNeedleEvent(Sender: TObject);
    procedure OnNeedleRedraw(Sender: TObject);
    procedure MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure MouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);    
    procedure NeedleMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure NeedleMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure NeedleMouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
    procedure DoRefresh(DrawBackground: Bool);
  published
    property BackColor: TColor read fBackColor write SetBackColor;
    property ForeColor: TColor read fForeColor write SetForeColor;
    property DoFade: Bool read fDoFade write SetDoFade;
    property ShowCenter: Bool read fShowCenter write SetShowCenter;
    property CenterColor: TColor read fCenterColor write SetCenterColor;
    property CenterSize: Integer read fCenterSize write SetCenterSize;
    property LabelTop: String read fLabelTop write SetLabelTop;
    property LabelBottom: String read fLabelBottom write SetLabelBottom; 
    property Needles: TNeedles read fNeedles write fNeedles;
    property Picture: TPicture read fPicture write fPicture;
    property FadeStep: Single read fFadeStep write SetFadeStep;
    property Anchors;
    property Align;
    property Visible;
    property OnGMouseDown: TJDMouseEvent read fMouseDown write fMouseDown;
    property OnGMouseUp: TJDMouseEvent read fMouseUp write fMouseUp;
    property OnGMouseMove: TJDMouseMoveEvent read fMouseMove write fMouseMove;
    property OnGNeedleMouseDown: TJDMouseEvent read fNeedleMouseDown write fNeedleMouseDown;
    property OnGNeedleMouseUp: TJDMouseEvent read fNeedleMouseUp write fNeedleMouseUp;
    property OnGNeedleMouseMove: TJDMouseMoveEvent read fNeedleMouseMove write fNeedleMouseMove;
    property OnClick;
    property OnDblClick;
  end;
       
function NewPosition(Center: TPoint; Distance: Integer; Degrees: Integer): TPoint;
procedure Register;

implementation
{$R JDNeedleGauge.dcr}

procedure Register;
begin
  RegisterComponents('JD Custom', [TJDNeedleGauge]);
end;        

function NewPosition(Center: TPoint; Distance: Integer; Degrees: Integer): TPoint;
var
  Radians: Real;
begin
  //Convert angle from degrees to radians; Subtract 135 to bring position to 0 Degrees
  Radians:= ((Degrees - 135) * Pi / 180.0);
  Result.X:= Trunc(Distance*Cos(Radians)-Distance*Sin(Radians))+Center.X;
  Result.Y:= Trunc(Distance*Sin(Radians)+Distance*Cos(Radians))+Center.Y;
end;

function IntRange(Value: Integer; Min: Integer; Max: Integer): Integer;
begin
  Result:= Value;
  if Result < Min then Result:= Min;
  if Result > Max then Result:= Max;
end;

{ THotspots }

function THotspots.GetHotspot(Point: TPoint): THotspot;
var
  H, D: String;
  P, P2: Integer;
begin
  D:= IntToStr(Point.X)+'x'+IntToStr(Point.Y)+',';
  P:= Pos(D, fHotspots);
  if P > 0 then begin
    H:= fHotspots;
    Result.Point:= Point;
    Result.Active:= True;
    Delete(H, 0, P+Length(D));
    P2:= Pos(',', H);
    Result.Size:= StrToIntDef(Copy(H, 0, P2), 0);
    Delete(H, 0, P2+1);
    P2:= Pos(';', H);
    Result.Value:= Copy(H, 0, P2);
  end else begin
    Result.Point:= Point;
    Result.Active:= False;
    Result.Size:= 0;
    Result.Value:= '';
  end;
end;

procedure THotspots.SetHotspot(Point: TPoint; Hotspot: THotspot);  
begin
  if Self.GetHotspot(Point).Active = False then begin
    Self.fHotspots:= Self.fHotspots + IntToStr(Point.X)+'x'+IntToStr(Point.Y)+
      ','+IntToStr(Hotspot.Size)+','+Hotspot.Value+';';
  end;
end;

constructor THotspots.Create;
begin
  Self.fHotspots:= '';
end;

destructor THotspots.Destroy;
begin

end;

procedure THotspots.Clear;
begin
  fHotspots:= '';
end;

function THotspots.InHotspot(APoint: TPoint): THotspot;
var
  Lst: TStringList;
  X, P: Integer;
  SX, SY, SS: String;
  TX, TY, TS: Integer;
  V: String;
begin
  if Length(fHotspots) > 0 then begin
    Lst:= TStringList.Create;
    try
      Lst.Delimiter:= ';';
      Lst.DelimitedText:= fHotspots;
      for X:= 0 to Lst.Count - 1 do begin
        V:= Lst[X];
        P:= Pos('x', V);
        SX:= Copy(V, 1, P-1);
        TX:= StrToIntDef(SX, 0);
        Delete(V, 1, P);
        P:= Pos(',', V);
        SY:= Copy(V, 1, P-1);
        TY:= StrToIntDef(SY, 0);
        Delete(V, 1, P);
        P:= Pos(',', V);
        SS:= Copy(V, 1, P-1);
        TS:= StrToIntDef(SS, 1);
        if (  (APoint.X > (TX - (TS div 2)) - 1)
          and (APoint.X < (TX + (TS div 2)) + 1)
          and (APoint.Y > (TY - (TS div 2)) - 1)
          and (APoint.Y < (TY + (TS div 2)) + 1)  ) then
        begin
          Result.Point:= Point(TX, TY);
          Result.Size:= TS;
          Result.Active:= True;
          Delete(V, 1, P);
          Result.Value:= Copy(V, P, Length(V));
        end else begin
          Result.Point:= APoint;
          Result.Active:= False;
          Result.Size:= 0;
          Result.Value:= Lst[X];
        end;
      end;
    finally
      Lst.Free;
    end;
  end;
end;

{ TNeedles }

constructor TNeedles.Create(AOwner: TComponent);
begin
  inherited Create(TNeedle);
  fOwner:= AOwner;
end;

function TNeedles.GetOwner: TPersistent;
begin
  Result:= fOwner;
end;

function TNeedles.Add: TNeedle;
begin
  Result:= Inherited Add as TNeedle;
  if assigned(Result) then begin
    Result.fOwner:= Self.fOwner;
    Result.OnEvent:= NeedleEvent;
    Result.OnRedraw:= NeedleRedraw;
    NeedleEvent(Self);
  end;
end;

procedure TNeedles.NeedleEvent(Sender: TObject);
begin
  if assigned(fEvent) then fEvent(Self);
end;

function TNeedles.GetItem(Index: Integer): TNeedle;
begin
  Result:= inherited GetItem(Index) as TNeedle;
end;

procedure TNeedles.SetItem(Index: Integer; Value: TNeedle);
begin
  inherited SetItem(Index, Value);
end;

procedure TNeedles.Update(Item: TNeedle);
begin
  inherited Update(Item);
end;

function TNeedles.Insert(Index: Integer): TNeedle;
begin
  Result:= inherited Insert(Index) as TNeedle;
end;

procedure TNeedles.NeedleRedraw(Sender: TObject);
begin
  if assigned(Self.fRedraw) then Self.fRedraw(Self);
end;

{ TNeedle }

constructor TNeedle.Create(AOwner: TCollection);
begin
  inherited Create(AOwner);
  Self.fOwner:= AOwner.Owner;
  Self.fTickMarks:= TTickMarks.Create(TNeedle(AOwner).fOwner);
  Self.fPicture:= TPicture.Create;
  Self.fHotspots:= THotspots.Create;
  Self.fLength:= 50;
  Self.fWidth:= 3;
  Self.fMin:= 0;
  Self.fMax:= 100;
  Self.fDegreeStart:= 0;
  Self.fDegreeStop:= 360;
  Self.fColor:= clBlue;
  Self.fTransparentColor:= clRed;
  Self.fPosition:= 0;
  Self.fCanClick:= False;
  Self.fSizing:= gnsAuto;
  Self.fPassMax:= False;
  Self.fVisible:= True;
  Self.DoEvent;
end;

destructor TNeedle.Destroy;
begin
  if assigned(Self.fPicture) then Self.fPicture.Free;
  if assigned(Self.fHotspots) then Self.fHotspots.Free;
  if assigned(fTickMarks) then fTickMarks.Free;
  inherited Destroy;
end;

procedure TNeedle.SetPicture(Value: TPicture);
begin
  fPicture.Assign(Value);
  Self.DoEvent;
end;

procedure TNeedle.SetLength(Value: Integer);
begin
  fLength:= Value;  
  Self.DoEvent;
end;

procedure TNeedle.SetWidth(Value: Integer);
begin
  fWidth:= Value;   
  Self.DoEvent;
end;

procedure TNeedle.SetColor(Value: TColor);
begin
  fColor:= Value;   
  Self.DoEvent;
end;

procedure TNeedle.SetTransparentColor(Value: TColor);
begin
  fTransparentColor:= Value; 
  Self.DoEvent;
end;

procedure TNeedle.DoEvent;
begin
  TJDNeedleGauge(Self.fOwner).DoRefresh(False);
  if assigned(Self.fEvent) then Self.fEvent(Self);
end;

procedure TNeedle.SetStyle(Value: TJDGNeedleStyle);
begin
  Self.fStyle:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetCursor(Value: TCursor);
begin
  Self.fCursor:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetPosition(Value: Integer);
begin
  Self.fPosition:= Value;
  Self.DoEvent;
end;
        
procedure TNeedle.SetMin(Value: Integer);  
begin
  Self.fMin:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetMax(Value: Integer);
begin
  Self.fMax:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetDegreeStart(Value: Integer);
begin
  Self.fDegreeStart:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetDegreeStop(Value: Integer);
begin
  Self.fDegreeStop:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetTickMarks(Value: TTickMarks);
begin
  Self.fTickMarks:= TTickMarks(Value);
  Self.DoEvent;
end;

procedure TNeedle.SetSizing(Value: TJDGNeedleSizing);
begin
  fSizing:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetPassMax(Value: Bool);
begin
  fPassMax:= Value;
  Self.DoEvent;
end;

procedure TNeedle.SetVisible(Value: Bool);
begin
  Self.fVisible:= Value;
  Self.DoEvent;
end;

function TNeedle.GetDisplayName: String;
begin
  Result:= 'Needle #'+IntToStr(Self.Index);
end;

procedure TNeedle.SetIndex(Value: Integer);
begin
  inherited;
  Self.DoEvent;
end;

procedure TNeedle.DoRedraw;
begin
  if assigned(Self.fRedraw) then Self.fRedraw(Self);
end;

procedure TNeedle.TickEvent(Sender: TObject);
begin
  if assigned(fEvent) then fEvent(Self);
end;

procedure TNeedle.TickRedraw(Sender: TObject);
begin
  if assigned(fRedraw) then fRedraw(Self);
end;

{ TTickMarks }

constructor TTickMarks.Create(AOwner: TPersistent);
begin
  inherited Create(TTickMark);
  fOwner:= TJDNeedleGauge(AOwner);
end;

function TTickMarks.GetOwner: TPersistent;
begin
  Result:= inherited GetOwner;
end;

function TTickMarks.Add: TTickMark;
begin
  Result:= Inherited Add as TTickMark;
  if assigned(Result) then begin
    Result.fOwner:= TJDNeedleGauge(Self.fOwner);
    Result.OnEvent:= Self.GaugeEvent;
    Result.OnRedraw:= Self.GaugeRedraw;
    Self.GaugeEvent(Self);
  end;
end;

procedure TTickMarks.GaugeEvent(Sender: TObject);
begin
  if assigned(fEvent) then fEvent(Self);
end;

function TTickMarks.GetItems(Index: Integer): TTickMark;
begin
  Result:= TTickMark(Inherited GetItem(Index));
end;

procedure TTickMarks.SetItems(Index: Integer; Value: TTickMark);
begin
  Inherited SetItem(Index, Value);
end;

procedure TTickMarks.GaugeRedraw(Sender: TObject);
begin
  if assigned(fRedraw) then fRedraw(Self);
end;

{ TTickMark }

constructor TTickMark.Create(AOwner: TCollection);
begin
  inherited Create(AOwner);
  Self.fHotspots:= THotspots.Create;
  Self.fIncrement:= 10;
  Self.fSize:= 5;
  Self.fColor:= clGray;
  Self.fCanClick:= False;
  Self.fVisible:= True;
end;

destructor TTickMark.Destroy;
begin
  if assigned(fHotspots) then fHotspots.Free;
  inherited Destroy;
end;

procedure TTickMark.SetIncrement(Value: Integer);
begin
  Self.fIncrement:= Value;
  Self.DoRedraw;
end;

procedure TTickMark.SetSize(Value: Integer);
begin
  Self.fSize:= Value;
  Self.DoRedraw;
end;

procedure TTickMark.SetColor(Value: TColor);
begin
  Self.fColor:= Value;
  Self.DoRedraw;
end;

procedure TTickMark.SetCanClick(Value: Bool);
begin
  Self.fCanClick:= Value;
  Self.DoRedraw;
end;
    
procedure TTickMark.DoEvent;
begin
  if assigned(Self.fEvent) then Self.fEvent(Self);
end;

procedure TTickMark.Assign(Source: TPersistent);
begin
  inherited Assign(TPersistent(Source));
end;
      
procedure TTickMark.DoRedraw;
begin
  if assigned(Self.fRedraw) then Self.fRedraw(Self);
end;

procedure TTickMark.SetVisible(Value: Bool);
begin
  fVisible:= Value;
  Self.DoRedraw;
end;

function TTickMark.GetDisplayName: String;
begin
  Result:= 'Tick Marks #'+IntToStr(Self.Index);
end;

{ TJDNeedleGauge }

const
  BitmapPixelFormat = pf32bit;

constructor TJDNeedleGauge.Create(AOwner: TComponent);
var
  N: TNeedle;
  T: TTickMark;
begin
  inherited Create(AOwner);
  //if (assigned(AOwner)) and (AOwner <> nil) then
    Self.Parent:= TWinControl(AOwner);
  fPicture:= TPicture.Create;
  fBmp:= TBitmap.Create;    
    fBmp.PixelFormat:= BitmapPixelFormat; 
    fBmp.TransparentColor:= clFuchsia;
    fBmp.Transparent:= True;
  fBackground:= TBitmap.Create;   
    fBackground.PixelFormat:= BitmapPixelFormat;
    fBackground.TransparentColor:= clFuchsia;
    fBackground.Transparent:= True;
  Self.fNeedles:= TNeedles.Create(Self);
    fNeedles.fOwner:= TJDNeedleGauge(Self);
    fNeedles.OnEvent:= Self.OnNeedleEvent;
    fNeedles.OnRedraw:= Self.OnNeedleRedraw;
  Self.Width:= 200;
  Self.Height:= 200;
  Self.fBackColor:= clBlack;
  Self.fForeColor:= clNavy;    
  Self.fFadeStep:= 1;
  Self.OnMouseDown:= Self.MouseDown;
  Self.OnMouseUp:= Self.MouseUp;
  Self.OnMouseMove:= Self.MouseMove;
  N:= TNeedle(fNeedles.Add);
    N.Min:= 0;
    N.Max:= 100;
    N.Position:= 0;
    T:= TTickMark(N.TickMarks.Add);
  Self.DoRefresh(True);
end;

destructor TJDNeedleGauge.Destroy;
begin
  if assigned(fNeedles) then fNeedles.Free;
  if assigned(fBmp) then fBmp.Free;
  if assigned(fBackground) then fBackground.Free;
  if assigned(fPicture) then fPicture.Free;
  inherited Destroy;
end;

procedure TJDNeedleGauge.DoRefresh(DrawBackground: Bool);
begin
  //Refresh entire gauge
  if DrawBackground then begin
    CreateBackground;
    DrawScale;
  end;
  DisplayGauge;
end;
        
procedure TJDNeedleGauge.DisplayGauge;
begin
  if fBmp.Width <> Width then
    fBmp.Width:= Width;
  if fBmp.Height <> Height then
    fBmp.Height:= Height;
  fBmp.Canvas.Brush.Color:= Self.fBackColor;
  fBmp.Canvas.FillRect(Rect(0,0,Width,Height));//clFuchsia
  fBmp.Canvas.Draw(0,0,fBackground);
  DrawNeedles;
  if Self.fShowCenter then DrawCenter;
  Canvas.Draw(0,0,fBmp);
end;

procedure TJDNeedleGauge.CreateBackground;
var
  R, G, B, TR, TG, TB: Integer;
  W, H, Sz, L, S, X: Integer;
  Count: Integer;
  Stop: Single;
begin
  //Create background image (fBackground)
  W:= Self.Width;
  H:= Self.Height;
  if W > H then Sz:= H else Sz:= W;
  fBackground.Width:= Sz;
  fBackground.Height:= Sz;
  fBackground.Canvas.Pen.Width:= 1;
  fBackground.Canvas.Brush.Color:= fBackColor;
  fBackground.Canvas.Pen.Color:= fBackColor;
  fBackground.Canvas.FillRect(Rect(0,0,W,H));
  if Self.fDoFade then begin
    L:= IntRange(Trunc((Sz / 2) - 1), 1, 1000);   //Radius
    R:= ( fForeColor and $ff);                    //Red
    G:= ((fForeColor and $ff00) shr 8);           //Green
    B:= ((fForeColor and $ff0000) shr 16);        //Blue
    Count:= 0;                                    //Set count to 0
    Stop:= L / fFadeStep;
    fBackground.Canvas.Brush.Color:= fForeColor;
    fBackground.Canvas.Pen.Color:= fForeColor;
    for X:= 0 to L do begin
      if (Count * fFadeStep) >= Stop then begin  
        Count:= 0;
        S:= Trunc((X / L) * 70);
        TR:= IntRange(R + S, 0, 255);
        TG:= IntRange(G + S, 0, 255);
        TB:= IntRange(B + S, 0, 255);
        fBackground.Canvas.Brush.Color:= RGB(TR,TG,TB);
        fBackground.Canvas.Pen.Color:= RGB(TR,TG,TB);
        fBackground.Canvas.Ellipse(5 + X, 5 + X, Sz - 5 - X, Sz - 5 - X);
      end else begin
        Count:= Count + 1;
      end;
    end;
  end else begin
    fBackground.Canvas.Brush.Color:= fForeColor;
    fBackground.Canvas.Pen.Color:= fForeColor;
    fBackground.Canvas.Ellipse(5, 5, Sz - 5, Sz - 5);
  end;
  Self.DrawScale;
end;

procedure TJDNeedleGauge.DrawScale;
var
  Center, NewPos: TPoint;
  D, G, C: Single;
  X, Y, Z, Sz, SSize, LSize, TickCount, I, J: Integer;
  N: TNeedle;
  T: TTickMark;
  H: THotspot;
begin
  //Draw scale around circle (fScale)  
  if Width >= Height then Sz:= Height else Sz:= Width;
  Center.X:= Sz div 2;
  Center.Y:= Sz div 2;
  for I:= 0 to fNeedles.Count - 1 do begin
    N:= fNeedles.Items[I];
    for J:= 0 to N.fTickMarks.Count - 1 do begin
      T:= TTickMark(N.fTickMarks.Items[J]);
      T.fHotspots.Clear;  
      TickCount:= (N.fMax - N.fMin) div T.fIncrement;
      D:= (Sz / 3) - (T.Size / 2) - 1;
      G:= N.fDegreeStart;
      fBackground.Canvas.Pen.Color:= T.Color;
      fBackground.Canvas.Pen.Style:= psSolid;
      fBackground.Canvas.Pen.Width:= T.Size;
      if TickCount = 0 then TickCount:= 1;
      C:= (N.DegreeStop - N.DegreeStart) / TickCount;
      for Z:= 0 to TickCount do begin
        NewPos:= NewPosition(Center, Trunc(D), Trunc(G));
        fBackground.Canvas.Ellipse(
          NewPos.X - (T.fSize div 2),
          NewPos.Y - (T.fSize div 2),
          NewPos.X + (T.fSize div 2) + 1,
          NewPos.Y + (T.fSize div 2) + 1);
        if T.fCanClick then begin
          for X:= (NewPos.X - (T.fSize div 2)) to (NewPos.X + (T.fSize div 2)) do begin
            for Y:= (NewPos.Y - (T.fSize div 2)) to (NewPos.Y + (T.fSize div 2)) do begin
              H.Point:= Point(X,Y);
              H.Size:= 1;
              H.Active:= True;
              H.Value:= IntToStr(Z);
              T.fHotspots.Hotspot[Point(X,Y)]:= H;
            end;
          end;
        end;
        G:= G + C;
      end;
    end;
  end;
end;

procedure TJDNeedleGauge.DrawNeedles;
var
  X: Integer;
begin
  for X:= 0 to fNeedles.Count - 1 do begin
    if fNeedles.Items[X].fVisible then
      DrawNeedle(fNeedles.Items[X]);
  end;
end;

procedure TJDNeedleGauge.DrawNeedle(ANeedle: TNeedle);
var
  Center, NewPos, P: TPoint;
  C: Single;
  Sz, Deg, L, X, Y, Z, TP: Integer;
  N: TBitmap;
  H: THotspot;
begin
  //Draw needle pointing in proper position (fBmp)
  if Self.Width >= Self.Height then Sz:= Self.Height else Sz:= Self.Width;
  Center.X:= Sz div 2;
  Center.Y:= Sz div 2;
  if ANeedle.fPassMax then begin 
    TP:= ANeedle.fPosition;
  end else begin
    if ANeedle.fPosition > ANeedle.fMax then
      TP:= ANeedle.fMax
    else
      TP:= ANeedle.fPosition;
  end;
  Deg:= Trunc((ANeedle.fDegreeStop - ANeedle.fDegreeStart) *
    (TP / (ANeedle.fMax - ANeedle.fMin)));
  C:= (ANeedle.DegreeStop - ANeedle.DegreeStart);
  case ANeedle.fSizing of
    gnsAuto: begin
      L:= Trunc((Sz / 3) - 10);
    end;
    gnsPixels: begin
      L:= ANeedle.fLength;
    end;
    gnsPercent: begin
      L:= Trunc((Sz / 3) - (Sz * ANeedle.Length));
    end;
  end;
  NewPos:= NewPosition(Center, L, Deg);  
  fBmp.Canvas.Pen.Width:= ANeedle.fWidth;
  fBmp.Canvas.Pen.Color:= ANeedle.fColor;
  case ANeedle.fStyle of
    gnsLine: begin
      //Draw actual line
      fBmp.Canvas.MoveTo(Center.X, Center.Y);
      fBmp.Canvas.LineTo(NewPos.X, NewPos.Y);
      //Create hotspots (Low Performance - Need to tweak)
      if ANeedle.fCanClick then begin
        ANeedle.fHotspots.Clear;
        for Z:= 0 to L - 1 do begin
          P:= NewPosition(Center, Z, Deg);
          for X:= P.X - (ANeedle.Width div 2) to
            P.X + (ANeedle.Width div 2) do
          begin
            for Y:= P.Y - (ANeedle.Width div 2) to
              P.Y + (ANeedle.Width div 2) do
            begin
              H.Point:= Point(X,Y);
              H.Size:= ANeedle.fWidth;
              H.Active:= True;
              H.Value:= IntToStr(Z);
              ANeedle.fHotspots.Hotspot[Point(X,Y)]:= H;
            end;
          end;
        end;
      end;
    end;
    gnsImage: begin
      if assigned(ANeedle.fPicture) then begin
        N:= TBitmap.Create;
        try
          //Assign picture to bitmap            
        finally
          N.Free;
        end;
      end;
    end;
  end;
end;

procedure TJDNeedleGauge.DrawCenter;  
var
  R, G, B, TR, TG, TB: Integer;
  W, H, L, D, S, X: Integer;
  Sz: Integer;
begin
  W:= Self.Width;
  H:= Self.Height;
  if W > H then Sz:= H else Sz:= W;
  //Create center piece image (fBmp)
  L:= IntRange(Trunc((fCenterSize / 2) - 1), 1, 500);
  R:= (fCenterColor and $ff);
  G:= ((fCenterColor and $ff00) shr 8);
  B:= ((fCenterColor and $ff0000) shr 16);
  for X:= ((Sz div 2) - (fCenterSize div 2)) downto L do begin
    S:= Trunc((X / L) * 70);
    D:= L - X;
    TR:= IntRange(R + S, 0, 255);
    TG:= IntRange(G + S, 0, 255);
    TB:= IntRange(B + S, 0, 255);
    fBmp.Canvas.Brush.Color:= RGB(TR,TG,TB);
    fBmp.Canvas.Pen.Color:= RGB(TR,TG,TB);
    fBmp.Canvas.Ellipse(
      (Sz div 2) - fCenterSize + 5 + X,
      (Sz div 2) - fCenterSize + 5 + X,
      fCenterSize - 5 - X,
      fCenterSize - 5 - X);
  end;
end;

procedure TJDNeedleGauge.Paint;
begin
  inherited Paint;
  DoRefresh(True);
end;

procedure TJDNeedleGauge.SetBackColor(Value: TColor);
begin
  fBackColor:= Value;
  DoRefresh(True);
end;

procedure TJDNeedleGauge.SetForeColor(Value: TColor);
begin
  fForeColor:= Value;
  DoRefresh(True);
end;

procedure TJDNeedleGauge.SetShowCenter(Value: Bool);
begin
  Self.fShowCenter:= Value;
  DoRefresh(False);
end;

procedure TJDNeedleGauge.SetCenterColor(Value: TColor);
begin
  Self.fCenterColor:= Value;
  if Self.fShowCenter then DoRefresh(False);
end;

procedure TJDNeedleGauge.SetCenterSize(Value: Integer);
begin
  Self.fCenterSize:= Value;
  if Self.fShowCenter then DoRefresh(False);
end;
          
procedure TJDNeedleGauge.SetLabelTop(Value: String);   
begin
  Self.fLabelTop:= Value;
  Self.DoRefresh(True);
end;

procedure TJDNeedleGauge.SetLabelBottom(Value: String);
begin
  Self.fLabelBottom:= Value;
  Self.DoRefresh(True);
end;

procedure TJDNeedleGauge.SetFadeStep(Value: Single);
begin
  Self.fFadeStep:= Value;
  if Value < 0 then Value:= 1;
  if Value > 800 then Value:= 800;
  Self.DoRefresh(True);
end;

procedure TJDNeedleGauge.OnNeedleEvent(Sender: TObject);
begin
  DoRefresh(False);
end;
        
procedure TJDNeedleGauge.OnNeedleRedraw(Sender: TObject);
begin
  DoRefresh(True);
end;

procedure TJDNeedleGauge.MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  I: Integer;
  N: TNeedle; 
  D: Bool;
begin
  //Check for hotspots  
  D:= False;
  for I:= 0 to fNeedles.Count - 1 do begin
    N:= fNeedles.Items[I];
    if N.fCanClick then begin
      if N.fHotspots.InHotspot(Point(X,Y)).Active then begin
        D:= True;
        if assigned(Self.fNeedleMouseDown) then
          Self.fNeedleMouseDown(Sender, Button, Shift, X, Y);
      end;
    end;
  end;
  if not D then
    if assigned(Self.fMouseDown) then
      Self.fMouseDown(Sender, Button, Shift, X, Y);
end;

procedure TJDNeedleGauge.MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  I: Integer;
  N: TNeedle;
  D: Bool;
begin
  //Check for hotspots
  D:= False;
  for I:= 0 to fNeedles.Count - 1 do begin
    N:= fNeedles.Items[I];
    if N.fCanClick then begin
      if N.fHotspots.InHotspot(Point(X,Y)).Active then begin   
        D:= True;
        if assigned(Self.fNeedleMouseUp) then
          Self.fNeedleMouseUp(Sender, Button, Shift, X, Y);
      end;
    end;
  end;     
  if not D then
    if assigned(Self.fMouseUp) then
      Self.fMouseUp(Sender, Button, Shift, X, Y);
end;

procedure TJDNeedleGauge.MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  I: Integer;
  N: TNeedle;    
  D: Bool;
begin
  //Check for hotspots    
  D:= False;
  for I:= 0 to fNeedles.Count - 1 do begin
    N:= fNeedles.Items[I];
    if N.fCanClick then begin
      if N.fHotspots.InHotspot(Point(X,Y)).Active then begin 
        D:= True;
        if assigned(Self.fNeedleMouseMove) then
          Self.fNeedleMouseMove(Sender, Shift, X, Y);
      end;
    end;
  end;   
  if not D then
    if assigned(Self.fMouseMove) then
      Self.fMouseMove(Sender, Shift, X, Y);
end;

procedure TJDNeedleGauge.NeedleMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if assigned(Self.fNeedleMouseDown) then
    Self.fNeedleMouseDown(Sender, Button, Shift, X, Y);
end;

procedure TJDNeedleGauge.NeedleMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if assigned(Self.fNeedleMouseUp) then
    Self.fNeedleMouseUp(Sender, Button, Shift, X, Y);
end;

procedure TJDNeedleGauge.NeedleMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
  if assigned(Self.fNeedleMouseMove) then
    Self.fNeedleMouseMove(Sender, Shift, X, Y);
end;

procedure TJDNeedleGauge.SetDoFade(Value: Bool);
begin
  Self.fDoFade:= Value;
  Self.DoRefresh(True);
end;

end.
Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top