{********************************************************************}
{                                                                    }
{ written by TMS Software                                            }
{            copyright © 2013                                        }
{            Email : info@tmssoftware.com                            }
{            Web : http://www.tmssoftware.com                        }
{                                                                    }
{ The source code is given as is. The author is not responsible      }
{ for any possible damage done due to the use of this code.          }
{ The complete source code remains property of the author and may    }
{ not be distributed, published, given or sold in any form as such.  }
{ No parts of the source code can be included in any other component }
{ or application without written authorization of the author.        }
{********************************************************************}

unit FMX.TMSLedmeter;

interface

{$I TMSDEFS.INC}

uses
  FMX.Types, FMX.Objects, Classes, Types,
  UITypes, SysUtils, FMX.TMSBasecontrol, UIConsts, FMX.Styles
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics
  {$ENDIF}
  ;

const
  MAJ_VER = 1; // Major version nr.
  MIN_VER = 5; // Minor version nr.
  REL_VER = 0; // Release nr.
  BLD_VER = 0; // Build nr.

  // version history
  // v1.0.0.0 : first release
  // v1.0.4.2 : Reorganized packages
  // v1.0.5.0 : New : support for update 4
  // v1.0.5.1 : Improved : TMSIOS define
  // v1.0.5.2 : New : support for update 4 hotfix 1
  // v1.5.0.0 : New : support for XE4

type
  TTMSFMXLEDScope = class;

  TValueEvent = procedure(Sender: TObject; Value: Integer) of object;

  TChannelValueEvent = procedure (Sender: TObject; Channel, Value: Integer) of object;

  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXLEDMeter = class(TTMSFMXBaseShape)
  private
    FMouseDown: Boolean;
    FSteps: Integer;
    FValue: Integer;
    FActiveStopColor: TAlphaColor;
    FActiveStartColor: TAlphaColor;
    FStopColor: TAlphaColor;
    FStartColor: TAlphaColor;
    FPeakValue: Integer;
    FShowPeak: Boolean;
    FPeakColor: TAlphaColor;
    FOnValueClick: TValueEvent;
    FOnMoveValue: TValueEvent;
    procedure SetActiveStartColor(const Value: TAlphaColor);
    procedure SetActiveStopColor(const Value: TAlphaColor);
    procedure SetStartColor(const Value: TAlphaColor);
    procedure SetSteps(const Value: Integer);
    procedure SetStopColor(const Value: TAlphaColor);
    procedure SetValue(const Value: Integer);
    procedure SetPeakValue(const Value: Integer);
    procedure SetShowPeak(const Value: Boolean);
    procedure SetPeakColor(const Value: TAlphaColor);
  protected
    procedure Changed;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
  public
    procedure HandleMouseEvent(X, Y: Single; EventType: Integer);
    function XYToLED(X, Y: Single; r: TRectF): Integer;
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
    procedure Draw(ACanvas: TCanvas; R: TRectF);
  published
    property StartColor: TAlphaColor read FStartColor write SetStartColor default claDarkGreen;
    property StopColor: TAlphaColor read FStopColor write SetStopColor default $FF7A0B0B;
    property ActiveStartColor: TAlphaColor read FActiveStartColor write SetActiveStartColor default claLime;
    property ActiveStopColor: TAlphaColor read FActiveStopColor write SetActiveStopColor default clared;
    property Value: Integer read FValue write SetValue default 0;
    property Steps: Integer read FSteps write SetSteps default 50;

    property PeakValue: Integer read FPeakValue write SetPeakValue default 0;
    property ShowPeak: Boolean read FShowPeak write SetShowPeak default True;
    property PeakColor: TAlphaColor read FPeakColor write SetPeakColor default claWhite;

    property Stroke;
    property StrokeThickness;
    property StrokeCap;
    property StrokeDash;
    property StrokeJoin;

    property OnValueClick: TValueEvent read FOnValueClick write FOnValueClick;
    property OnMoveValue: TValueEvent read FOnMoveValue write FOnMoveValue;
  end;

  TTMSFMXLEDChannel = class(TCollectionItem)
  private
    FLedMeter: TTMSFMXLEDMeter;
    FOwner: TTMSFMXLEDScope;
    function GetActiveStartColor: TAlphaColor;
    function GetActiveStopColor: TAlphaColor;
    function GetPeakColor: TAlphaColor;
    function GetPeakValue: Integer;
    function GetShowPeak: Boolean;
    function GetStartColor: TAlphaColor;
    function GetSteps: Integer;
    function GetStopColor: TAlphaColor;
    function GetStroke: TBrush;
    function GetStrokeCap: TStrokeCap;
    function GetStrokeDash: TStrokeDash;
    function GetStrokeJoin: TStrokeJoin;
    function GetStrokeThickness: Single;
    function GetValue: Integer;
    procedure SetActiveStartColor(const Value: TAlphaColor);
    procedure SetActiveStopColor(const Value: TAlphaColor);
    procedure SetPeakColor(const Value: TAlphaColor);
    procedure SetPeakValue(const Value: Integer);
    procedure SetShowPeak(const Value: Boolean);
    procedure SetStartColor(const Value: TAlphaColor);
    procedure SetSteps(const Value: Integer);
    procedure SetStopColor(const Value: TAlphaColor);
    procedure SetStroke(const Value: TBrush);
    procedure SetStrokeCap(const Value: TStrokeCap);
    procedure SetStrokeDash(const Value: TStrokeDash);
    procedure SetStrokeJoin(const Value: TStrokeJoin);
    procedure SetStrokeThickness(const Value: Single);
    procedure SetValue(const Value: Integer);
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property StartColor: TAlphaColor read GetStartColor write SetStartColor;
    property StopColor: TAlphaColor read GetStopColor write SetStopColor;
    property ActiveStartColor: TAlphaColor read GetActiveStartColor write SetActiveStartColor;
    property ActiveStopColor: TAlphaColor read GetActiveStopColor write SetActiveStopColor;
    property Value: Integer read GetValue write SetValue;
    property Steps: Integer read GetSteps write SetSteps;

    property PeakValue: Integer read GetPeakValue write SetPeakValue;
    property ShowPeak: Boolean read GetShowPeak write SetShowPeak;
    property PeakColor: TAlphaColor read GetPeakColor write SetPeakColor;

    property Stroke: TBrush read GetStroke write SetStroke;
    property StrokeThickness: Single read GetStrokeThickness write SetStrokeThickness;
    property StrokeCap: TStrokeCap read GetStrokeCap write SetStrokeCap;
    property StrokeDash: TStrokeDash read GetStrokeDash write SetStrokeDash;
    property StrokeJoin: TStrokeJoin read GetStrokeJoin write SetStrokeJoin;
  end;

  TTMSFMXLEDChannels = class(TTMSFMXBaseCollection)
  private
    FOwner: TTMSFMXLEDScope;
    function GetItem(Index: Integer): TTMSFMXLEDChannel;
    procedure SetItem(Index: Integer; const Value: TTMSFMXLEDChannel);
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TTMSFMXLEDScope);
    function Add: TTMSFMXLEDChannel;
    function Insert(Index: Integer): TTMSFMXLEDChannel;
    property Items[Index: Integer]: TTMSFMXLEDChannel read GetItem write SetItem; default;
  end;

  TChannelRec = record
    Channel: Integer;
    ChannelR: TRectF;
  end;

  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXLEDScope = class(TTMSFMXBaseControl)
  private
    FMouseDown: Boolean;
    FChannels: TTMSFMXLEDChannels;
    FOnValueClick: TChannelValueEvent;
    FAnimation: Boolean;
    FOnMoveValue: TChannelValueEvent;
    procedure SetChannels(const Value: TTMSFMXLEDChannels);
    procedure SetAnimation(const Value: Boolean);
    function GetVersion: String;
    function GetVersionNr: Integer;
  protected
    function GetInstance: NativeUInt; override;
    procedure Changed;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure HandleMouseEvent(X, Y: Single; EventType: Integer);
  public
    procedure Paint; override;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function XYToChannel(X, Y: Single): TChannelRec;
  published
    property Version: String read GetVersion;
    property Animation: Boolean read FAnimation write SetAnimation default True;
    property Channels: TTMSFMXLEDChannels read FChannels write SetChannels;
    property OnValueClick: TChannelValueEvent read FOnValueClick write FOnValueClick;
    property OnMoveValue: TChannelValueEvent read FOnMoveValue write FOnMoveValue;
  end;

implementation

uses
  Math;

{ TTMSFMXLEDMeter }

procedure TTMSFMXLEDMeter.Changed;
begin
  Repaint;
end;

constructor TTMSFMXLEDMeter.Create(AOwner: TComponent);
begin
  inherited;
  FStartColor := claGreen;
  FStopColor := $FF7A0B0B;
  FActiveStartColor := claLime;
  FActiveStopColor := claRed;
  FValue := 0;
  FSteps := 50;

  FPeakValue := 0;
  FPeakColor := claWhite;
  FShowPeak := True;

  Height := 200;
  Width := 30;
end;

procedure TTMSFMXLEDMeter.Draw(ACanvas: TCanvas; R: TRectF);
var
  i: integer;
  h: Single;
  hstart, sstart, lstart: Single;
  hstop, sstop, lstop: Single;
  hstarta, sstarta, lstarta: Single;
  hstopa, sstopa, lstopa: Single;
  rt: TRectF;
  chk: Boolean;
begin
  if Steps > 0 then
  begin
    ACanvas.Stroke.Assign(Stroke);
    ACanvas.StrokeThickness := Stroke.Thickness;
    ACanvas.StrokeCap := Stroke.Cap;
    ACanvas.StrokeDash := Stroke.Dash;
    ACanvas.StrokeJoin := Stroke.Join;

    RGBtoHSL(StartColor, hstart, sstart, lstart);
    RGBtoHSL(StopColor, hstop, sstop, lstop);

    RGBtoHSL(ActiveStartColor, hstarta, sstarta, lstarta);
    RGBtoHSL(ActiveStopColor, hstopa, sstopa, lstopa);

    h := R.Height / Steps;
    {$IFDEF DELPHIXE6_LVL}
    ACanvas.Fill.Kind := TBrushKind.Solid;
    {$ELSE}
    ACanvas.Fill.Kind := TBrushKind.bkSolid;
    {$ENDIF}
    for I := 0 to Steps - 1 do
    begin

      chk := (PeakValue = (I + 1)) and (PeakValue > 0) and ShowPeak;

      if (I < Value) or ((PeakColor = claNull) and chk) then
        ACanvas.Fill.Color := HSLtoRGB(hstarta + ((hstopa - hstarta) / Steps * I), sstarta + ((sstopa - sstarta) / Steps * I), lstarta + ((lstopa - lstarta) / Steps * I))
      else
        ACanvas.Fill.Color := HSLtoRGB(hstart + ((hstop - hstart) / Steps * I), sstart+ ((sstop - sstart) / Steps * I), lstart + ((lstop - lstart) / Steps * I));

      if chk and (PeakColor <> claNull) then
        ACanvas.Fill.Color := PeakColor;

      rt := RectF(r.Left, r.Height - ((I + 1) * h), R.Width, r.Height - (I * h));

      ACanvas.fillRect(rt, 0, 0, AllCorners, AbsoluteOpacity);
      ACanvas.DrawRect(rt, 0, 0, AllCorners, AbsoluteOpacity);
    end;
  end;
end;

procedure TTMSFMXLEDMeter.HandleMouseEvent(X, Y: Single; EventType: Integer);
var
  val: Integer;
begin
  val := XYToLED(X, Y, BoundsRect);
  if val <> -1 then
  begin
    case EventType of
      0:
      begin
        if Assigned(OnValueClick) then
          OnValueClick(Self, val);
      end;
      1:
      begin
        if Assigned(OnMoveValue) then
          OnMoveValue(Self, val);
      end;
    end;
  end;
end;

procedure TTMSFMXLEDMeter.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  FMouseDown := True;
end;

procedure TTMSFMXLEDMeter.MouseMove(Shift: TShiftState; X, Y: Single);
begin
  inherited;
  if FMouseDown then
    HandleMouseEvent(X, Y, 1);
end;

procedure TTMSFMXLEDMeter.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  FMouseDown := False;
  HandleMouseEvent(X, Y, 0);
end;

procedure TTMSFMXLEDMeter.Paint;
begin
  inherited;
  Draw(Canvas, BoundsRect);
end;

procedure TTMSFMXLEDMeter.SetActiveStartColor(const Value: TAlphaColor);
begin
  if FActiveStartColor <> Value then
  begin
    FActiveStartColor := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetActiveStopColor(const Value: TAlphaColor);
begin
  if FActiveStopColor <> Value then
  begin
    FActiveStopColor := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetPeakValue(const Value: Integer);
begin
  if FPeakValue <> Value then
  begin
    FPeakValue := Max(0, Min(Steps, Value));
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetShowPeak(const Value: Boolean);
begin
  if FShowPeak <> Value then
  begin
    FShowPeak := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetPeakColor(const Value: TAlphaColor);
begin
  if FPeakColor <> Value then
  begin
    FPeakColor := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetStartColor(const Value: TAlphaColor);
begin
  if FStartColor <> Value then
  begin
    FStartColor := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetSteps(const Value: Integer);
begin
  if FSteps <> Value then
  begin
    FSteps := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetStopColor(const Value: TAlphaColor);
begin
  if FStopColor <> Value then
  begin
    FStopColor := Value;
    Changed;
  end;
end;

procedure TTMSFMXLEDMeter.SetValue(const Value: Integer);
begin
  if FValue <> Value then
  begin
    FValue := Value;
    if FValue > FSteps then
      FSteps := FValue;
    if FValue > PeakValue then
      PeakValue := Value;
    Changed;
  end;
end;

function TTMSFMXLEDMeter.XYToLED(X, Y: Single; r: TRectF): Integer;
var
  I: Integer;
  rr: TRectF;
begin
  Result := -1;
  if Steps > 0 then
  begin
    for I := 0 to Steps - 1 do
    begin
      rr := RectF(r.Left, R.Top + r.Height - ((r.Height - r.Top) / Steps) * (I + 1), r.Left +  r.Width, r.Top + r.Height - ((r.Height - r.Top) / Steps) * I);
      if PointInRect(PointF(X, Y), rr) then
      begin
        Result := (I + 1);
        Break;
      end;
    end;
  end;
end;

{ TTMSFMXChannel }

constructor TTMSFMXLEDChannel.Create(Collection: TCollection);
begin
  inherited;
  FOwner := (Collection as TTMSFMXLEDChannels).FOwner;
  FLedMeter := TTMSFMXLEDMeter.Create(FOwner);
  FOwner.Changed;
end;

destructor TTMSFMXLEDChannel.Destroy;
begin
  if Assigned(FLedMeter) then
    FLedMeter.Free;
  inherited;
  FOwner.Changed;
end;

function TTMSFMXLEDChannel.GetActiveStartColor: TAlphaColor;
begin
  Result := FLedMeter.ActiveStartColor;
end;

function TTMSFMXLEDChannel.GetActiveStopColor: TAlphaColor;
begin
  Result := FLedMeter.ActiveStopColor;
end;

function TTMSFMXLEDChannel.GetPeakColor: TAlphaColor;
begin
  Result := FLedMeter.PeakColor;
end;

function TTMSFMXLEDChannel.GetPeakValue: Integer;
begin
  Result := FLedMeter.PeakValue;
end;

function TTMSFMXLEDChannel.GetShowPeak: Boolean;
begin
  Result := FLedMeter.ShowPeak;
end;

function TTMSFMXLEDChannel.GetStartColor: TAlphaColor;
begin
  Result := FLedMeter.StartColor;
end;

function TTMSFMXLEDChannel.GetSteps: Integer;
begin
  Result := FLedMeter.Steps;
end;

function TTMSFMXLEDChannel.GetStopColor: TAlphaColor;
begin
  Result := FLedMeter.StopColor;
end;

function TTMSFMXLEDChannel.GetStroke: TBrush;
begin
  Result := FLedMeter.Stroke;
end;

function TTMSFMXLEDChannel.GetStrokeCap: TStrokeCap;
begin
  Result := FLedMeter.Stroke.Cap;
end;

function TTMSFMXLEDChannel.GetStrokeDash: TStrokeDash;
begin
  Result := FLedMeter.Stroke.Dash;
end;

function TTMSFMXLEDChannel.GetStrokeJoin: TStrokeJoin;
begin
  Result := FLedMeter.Stroke.Join;
end;

function TTMSFMXLEDChannel.GetStrokeThickness: Single;
begin
  Result := FLedMeter.Stroke.Thickness;
end;

function TTMSFMXLEDChannel.GetValue: Integer;
begin
  Result := FLedMeter.Value;
end;

procedure TTMSFMXLEDChannel.SetActiveStartColor(const Value: TAlphaColor);
begin
  FLedMeter.ActiveStartColor := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetActiveStopColor(const Value: TAlphaColor);
begin
  FLedMeter.ActiveStopColor := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetPeakColor(const Value: TAlphaColor);
begin
  FLedMeter.PeakColor := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetPeakValue(const Value: Integer);
begin
  FLedMeter.PeakValue := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetShowPeak(const Value: Boolean);
begin
  FLedMeter.ShowPeak := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStartColor(const Value: TAlphaColor);
begin
  FLedMeter.StartColor := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetSteps(const Value: Integer);
begin
  FLedMeter.Steps := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStopColor(const Value: TAlphaColor);
begin
  FLedMeter.StopColor := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStroke(const Value: TBrush);
begin
  FLedMeter.Stroke.Assign(Value);
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStrokeCap(const Value: TStrokeCap);
begin
  FLedMeter.StrokeCap := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStrokeDash(const Value: TStrokeDash);
begin
  FLedMeter.StrokeDash := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStrokeJoin(const Value: TStrokeJoin);
begin
  FLedMeter.StrokeJoin := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetStrokeThickness(const Value: Single);
begin
  FLedMeter.StrokeThickness := Value;
  FOwner.Changed;
end;

procedure TTMSFMXLEDChannel.SetValue(const Value: Integer);
begin
  FLedMeter.Value := Value;
  FOwner.Changed;
end;

{ TTMSFMXChannels }

function TTMSFMXLEDChannels.Add: TTMSFMXLEDChannel;
begin
  Result := TTMSFMXLEDChannel(inherited Add);
end;

constructor TTMSFMXLEDChannels.Create(AOwner: TTMSFMXLEDScope);
begin
  inherited Create(TTMSFMXLEDChannel);
  FOwner := AOwner;
end;

function TTMSFMXLEDChannels.GetItem(Index: Integer): TTMSFMXLEDChannel;
begin
  Result := TTMSFMXLEDChannel(inherited Items[Index]);
end;

function TTMSFMXLEDChannels.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

function TTMSFMXLEDChannels.Insert(Index: Integer): TTMSFMXLEDChannel;
begin
  Result := TTMSFMXLEDChannel(inherited Insert(Index));
end;

procedure TTMSFMXLEDChannels.SetItem(Index: Integer; const Value: TTMSFMXLEDChannel);
begin
  inherited Items[Index] := Value;
end;

{ TTMSFMXLEDScope }

procedure TTMSFMXLEDScope.Changed;
begin
  Repaint;
end;

constructor TTMSFMXLEDScope.Create(AOwner: TComponent);
var
  i: integer;
begin
  inherited;
  FChannels := TTMSFMXLEDChannels.Create(Self);
  Width := 450;
  Height := 350;
  {$IFDEF DELPHIXE6_LVL}
  Fill.Kind := TBrushKind.Solid;
  {$ELSE}
  Fill.Kind := TBrushKind.bkSolid;
  {$ENDIF}

  if (csDesigning in ComponentState) and not (csLoading in ComponentState) then
  begin
    for I := 0 to 15 do
      FChannels.Add.Value := Random(50);
  end;
end;

destructor TTMSFMXLEDScope.Destroy;
begin
  FChannels.Free;
  inherited;
end;

function TTMSFMXLEDScope.GetInstance: NativeUInt;
begin
  Result := HInstance;
end;

function TTMSFMXLEDScope.GetVersion: String;
var
  vn: Integer;
begin
  vn := GetVersionNr;
  Result := IntToStr(Hi(Hiword(vn)))+'.'+IntToStr(Lo(Hiword(vn)))+'.'+IntToStr(Hi(Loword(vn)))+'.'+IntToStr(Lo(Loword(vn)));
end;

function TTMSFMXLEDScope.GetVersionNr: integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

procedure TTMSFMXLEDScope.HandleMouseEvent(X, Y: Single; EventType: Integer);
var
  chnr: TChannelRec;
  ledval: Integer;
begin
  chnr := XYToChannel(X, Y);
  if chnr.Channel <> -1 then
  begin
    ledval := Channels[chnr.Channel].FLedMeter.XYToLED(X, Y, chnr.ChannelR);
    if ledval <> -1 then
    begin
      case EventType of
        0:
        begin
          if Assigned(OnValueClick) then
            OnValueClick(Self, chnr.Channel, ledval);
        end;
        1:
        begin
          if Assigned(OnMoveValue) then
            OnMoveValue(Self, chnr.Channel, ledval);
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXLEDScope.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  FMouseDown := True;
end;

procedure TTMSFMXLEDScope.MouseMove(Shift: TShiftState; X, Y: Single);
begin
  inherited;
  if FMouseDown then
    HandleMouseEvent(X, Y, 1);
end;

procedure TTMSFMXLEDScope.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  FMouseDown := False;
  HandleMouseEvent(X, Y, 0);
end;

procedure TTMSFMXLEDScope.Paint;
var
  I: Integer;
  x, y, w, h: Single;
begin
  inherited;
  for I := 0 to Channels.Count - 1 do
  begin
    x := (Width / Channels.Count) * I;
    w := (Width / Channels.Count) * (I + 1);
    h := Height;
    y := 0;
    Channels[I].FLedMeter.Draw(Canvas, RectF(x, y, x + w, y + h));
  end;
end;

procedure TTMSFMXLEDScope.SetAnimation(const Value: Boolean);
begin
  if FAnimation <> Value then
    FAnimation := Value;
end;

procedure TTMSFMXLEDScope.SetChannels(const Value: TTMSFMXLEDChannels);
begin
  if FChannels <> Value then
    FChannels.Assign(Value);
end;

function TTMSFMXLEDScope.XYToChannel(X, Y: Single): TChannelRec;
var
  I: Integer;
  xt, yt, wt, ht: Single;
  r: TRectF;
begin
  Result.Channel := -1;
  for I := 0 to Channels.Count - 1 do
  begin
    xt := (Width / Channels.Count) * I;
    wt := (Width / Channels.Count) * (I + 1);
    ht := Height;
    yt := 0;
    r := RectF(xt, yt, wt, ht);
    if PointInRect(PointF(X, Y), r) then
    begin
      Result.Channel := I;
      Result.ChannelR := r;
      Break;
    end;
  end;
end;

end.
