{ A trackbar picker for Luminance or Value parameters from the HSL or HSV
  color models (depending on setting for BrightnessMode) }

unit LVColorPicker;

interface

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}

uses
  LCLIntf, LCLType, SysUtils, Classes, Controls, Graphics, Forms,
  HTMLColors, mbColorConv, mbTrackBarPicker;

type
  TLVColorPicker = class(TmbHSLVTrackBarPicker)
  private
    FHint: array[TBrightnessMode] of string;
    function ArrowPosFromLum(L: Double): integer;
    function ArrowPosFromVal(V: Double): integer;
    function LumFromArrowPos(p: integer): Double;
    function ValFromArrowPos(p: Integer): Double;
    function GetHint(AMode: TBrightnessMode): String;
    procedure SetHint(AMode: TBrightnessMode; AText: String); reintroduce;
  protected
    procedure Execute(tbaAction: integer); override;
    function GetArrowPos: integer; override;
    function GetGradientColor(AValue: Integer): TColor; override;
    function GetSelectedValue: integer; override;
    procedure SetBrightnessMode(AMode: TBrightnessMode); override;
    procedure SetMaxLum(L: Integer); override;
    procedure SetMaxVal(V: Integer); override;
    procedure SetRelLum(L: Double); override;
    procedure SetRelVal(V: Double); override;
    procedure SetSelectedColor(c: TColor); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property Hue default 0;
    property Saturation default 0;
    property Luminance default 255;
    property Value default 255;
    property SelectedColor default clWhite;
    property LHintFormat: String index bmLuminance read GetHint write SetHint;
    property VHintFormat: String index bmValue read GetHint write SetHint;
  end;

implementation

uses
  mbUtils;

{ TLVColorPicker }

constructor TLVColorPicker.Create(AOwner: TComponent);
begin
  inherited;
  case BrightnessMode of
    bmLuminance : FGradientWidth := FMaxLum + 1;
    bmValue     : FGradientWidth := FMaxVal + 1;
  end;
  FGradientHeight := 1;
  FHue := 0;
  FSat := 0;
  FLum := 1;
  FVal := 1;
  FHint[bmLuminance] := 'Luminance: %lum (selected)';
  FHint[bmValue] := 'Value: %value (selected)';
end;

function TLVColorPicker.ArrowPosFromLum(L: Double): integer;
var
  a: integer;
begin
  if Layout = lyHorizontal then
  begin
    a := Round((Width - 12) * L);
    if a > Width - FLimit then a := Width - FLimit;
  end
  else
  begin
    a := Round((Height - 12) * (1.0 - L));
    if a > Height - FLimit then a := Height - FLimit;
  end;
  if a < 0 then a := 0;
  Result := a;
end;

function TLVColorPicker.ArrowPosFromVal(V: Double): integer;
var
  a: integer;
begin
  if Layout = lyHorizontal then
  begin
    a := Round((Width - 12) * V);
    if a > Width - FLimit then a := Width - FLimit;
  end
  else
  begin
    a := Round((Height - 12) * (1.0 - V));
    if a > Height - FLimit then a := Height - FLimit;
  end;
  if a < 0 then a := 0;
  Result := a;
end;

procedure TLVColorPicker.Execute(tbaAction: integer);
var
  dLum, dVal: Double;
begin
  case BrightnessMode of
    bmLuminance:
      begin
        if FMaxLum = 0 then dLum := 0 else dLum := Increment / FMaxLum;
        case tbaAction of
          TBA_Resize:
            SetRelLum(FLum);
          TBA_MouseMove:
            SetRelLum(LumFromArrowPos(FArrowPos));
          TBA_MouseDown:
            SetRelLum(LumFromArrowPos(FArrowPos));
          TBA_MouseUp:
            SetRelLum(LumFromArrowPos(FArrowPos));
          TBA_WheelUp:
            SetRelLum(FLum + dLum);
          TBA_WheelDown:
            SetRelLum(FLum - dLum);
          TBA_VKRight:
            SetRelLum(FLum + dLum);
          TBA_VKCtrlRight:
            SetRelLum(1.0);
          TBA_VKLeft:
            SetRelLum(FLum - dLum);
          TBA_VKCtrlLeft:
            SetRelLum(0.0);
          TBA_VKUp:
            SetRelLum(FLum + dLum);
          TBA_VKCtrlUp:
            SetRelLum(1.0);
          TBA_VKDown:
            SetRelLum(FLum - dLum);
          TBA_VKCtrlDown:
            SetRelLum(0);
          else
            inherited;
        end;
      end;

    bmValue:
      begin
        if FMaxVal = 0 then dVal := 0 else dVal := Increment / FMaxVal;
        case tbaAction of
          TBA_Resize:
            SetRelVal(FVal);
          TBA_MouseMove:
            SetRelVal(ValFromArrowPos(FArrowPos));
          TBA_MouseDown:
            SetRelVal(ValFromArrowPos(FArrowPos));
          TBA_MouseUp:
            SetRelVal(ValFromArrowPos(FArrowPos));
          TBA_WheelUp:
            SetRelVal(FVal + dVal);
          TBA_WheelDown:
            SetRelVal(FVal - dVal);
          TBA_VKRight:
            SetRelval(FVal + dVal);
          TBA_VKCtrlRight:
            SetRelVal(1.0);
          TBA_VKLeft:
            SetRelval(FVal - dVal);
          TBA_VKCtrlLeft:
            SetRelVal(0.0);
          TBA_VKUp:
            SetRelVal(FVal + dVal);
          TBA_VKCtrlUp:
            SetRelVal(1.0);
          TBA_VKDown:
            SetRelval(FVal - dVal);
          TBA_VKCtrlDown:
            SetRelVal(0.0);
          else
            inherited;
        end;
      end;
  end;
end;

function TLVColorPicker.GetArrowPos: integer;
begin
  case BrightnessMode of
    bmLuminance:
      if FMaxLum = 0 then
        Result := inherited GetArrowPos
      else
        Result := ArrowPosFromLum(FLum);
    bmValue:
      if FMaxVal = 0 then
        Result := inherited GetArrowPos
      else
        Result := ArrowPosFromVal(FVal);
  end;
end;

function TLVColorPicker.GetGradientColor(AValue: Integer): TColor;
begin
  Result := HSLVtoColor(FHue, FSat, AValue/FMaxLum, AValue/FMaxVal);
end;

function TLVColorPicker.GetHint(AMode: TBrightnessMode): String;
begin
  Result := FHint[AMode];
end;

function TLVColorPicker.GetSelectedValue: integer;
begin
  case BrightnessMode of
    bmLuminance : Result := Luminance;
    bmValue     : Result := Value;
  end;
end;

function TLVColorPicker.LumFromArrowPos(p: integer): Double;
var
  L: Double;
begin
  case Layout of
    lyHorizontal : L :=       p / (Width - 12);
    lyVertical   : L := 1.0 - p /(Height - 12);
  end;
  Clamp(L, 0, 1.0);
  Result := L;
end;

procedure TLVColorPicker.SetBrightnessMode(AMode: TBrightnessMode);
begin
  inherited;
  HintFormat := FHint[AMode];
end;

procedure TLVColorPicker.SetHint(AMode: TBrightnessMode; AText: String);
begin
  FHint[AMode] := AText;
end;

procedure TLVColorPicker.SetMaxLum(L: Integer);
begin
  if L = FMaxLum then
    exit;
  FMaxLum := L;
  if BrightnessMode = bmLuminance then begin
    FGradientWidth := FMaxLum + 1;
    CreateGradient;
    Invalidate;
    DoChange;
  end;
end;

procedure TLVColorPicker.SetMaxVal(V: Integer);
begin
  if V = FMaxVal then
    exit;
  FMaxVal := V;
  if BrightnessMode = bmValue then begin
    FGradientWidth := FMaxVal + 1;
    CreateGradient;
    Invalidate;
    DoChange;
  end;
end;

procedure TLVColorPicker.SetRelLum(L: Double);
begin
  Clamp(L, 0, 1.0);
  if FLum <> L then
  begin
    FLum := L;
    FArrowPos := ArrowPosFromLum(L);
    Invalidate;
    DoChange;
  end;
end;

procedure TLVColorPicker.SetRelVal(V: Double);
begin
  Clamp(V, 0, 1.0);
  if FVal <> V then
  begin
    FVal := V;
    FArrowPos := ArrowPosFromVal(V);
    Invalidate;
    DoChange;
  end;
end;

procedure TLVColorPicker.SetSelectedColor(c: TColor);
var
  H: Double = 0;
  S: Double = 0;
  L: Double = 0;
  V: Double = 0;
  needNewGradient: Boolean;
begin
  if WebSafe then
    c := GetWebSafe(c);
  if c = GetSelectedColor then
    exit;

  ColorToHSLV(c, H, S, L, V);
  needNewGradient := (H <> FHue) or (S <> FSat);
  FHue := H;
  FSat := S;
  case BrightnessMode of
    bmLuminance : FLum := L;
    bmValue     : FVal := V;
  end;
  if needNewGradient then
    CreateGradient;
  Invalidate;
  DoChange;
end;

function TLVColorPicker.ValFromArrowPos(p: integer): Double;
var
  V: Double;
begin
  case Layout of
    lyHorizontal : V :=       p / (Width - 12);
    lyVertical   : V := 1.0 - p /(Height - 12);
  end;
  Clamp(V, 0, 1.0);
  Result := V;
end;

end.