{*********************************************}
{  TeeChart Charting Library for FireMonkey   }
{  Copyright (c) 2012-2020 by Steema Software }
{  All Rights Reserved                        }
{*********************************************}
unit FMXTee.Editor.Gradient;
{$I TeeDefs.inc}

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms,
  {$IFNDEF UPTOXE3}
  FMX.StdCtrls,
  {$ENDIF}
  FMX.Dialogs, FMX.ListBox, FMX.TabControl, FMXTee.Canvas,
  FMXTee.Procs, FMX.Colors, FMX.Layouts, FMXTee.Chart.GalleryPanel;

type
  TGradientEditor = class(TForm)
    Panel1: TPanel;
    CBVisible: TCheckBox;
    Label1: TLabel;
    CBDirection: TComboBox;
    ListBoxItem1: TListBoxItem;
    ListBoxItem2: TListBoxItem;
    ListBoxItem3: TListBoxItem;
    ListBoxItem4: TListBoxItem;
    ListBoxItem5: TListBoxItem;
    ListBoxItem6: TListBoxItem;
    ListBoxItem7: TListBoxItem;
    ListBoxItem8: TListBoxItem;
    ListBoxItem9: TListBoxItem;
    ListBoxItem10: TListBoxItem;
    TabControl1: TTabControl;
    TabItem1: TTabItem;
    TabItem2: TTabItem;
    ColorButton1: TComboColorBox;
    ColorButton2: TComboColorBox;
    ColorButton3: TComboColorBox;
    CBUseMiddle: TCheckBox;
    Button1: TButton;
    GradientEdit1: TGradientEdit;
    GroupBox1: TGroupBox;
    TBX: TTrackBar;
    TBY: TTrackBar;
    Label2: TLabel;
    Label3: TLabel;
    TabDirections: TTabItem;
    Directions: TChartGalleryPanel;
    TabGallery: TTabItem;
    Gallery: TChartGalleryPanel;
    CurrentColor: TComboColorBox;
    BDeleteColor: TButton;
    PanelButtons: TPanel;
    Layout1: TLayout;
    Button2: TButton;
    Button3: TButton;
    procedure CBVisibleChange(Sender: TObject);
    procedure ColorButton1Change(Sender: TObject);
    procedure ColorButton2Change(Sender: TObject);
    procedure ColorButton3Change(Sender: TObject);
    procedure CBDirectionChange(Sender: TObject);
    procedure CBUseMiddleChange(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure TBXChange(Sender: TObject);
    procedure TBYChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure GradientEdit1Change(Sender: TObject);
    procedure GradientEdit1SelectPoint(Sender: TObject);
    procedure TabControl1Change(Sender: TObject);
    procedure GalleryChangeChart(Sender: TObject);
    procedure DirectionsChangeChart(Sender: TObject);
    procedure CurrentColorChange(Sender: TObject);
    procedure BDeleteColorClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
    Gradient : TCustomTeeGradient;
    IBackup : TCustomTeeGradient;

    IModified : Boolean;
    IUpdating : Boolean;
    IChangingButtons : Boolean;
    IChangingColor : Boolean;

    procedure ApplyChart(const Chart:TGalleryChart);
    class procedure DefaultGradient(const Gradient:TCustomTeeGradient;
                                    IsRectGradient:Boolean; Index:Integer);
    Function IsRectGradient:Boolean;
    procedure RefreshControls;
    procedure SelectDirection;
    procedure SetDirectionGradients;
    procedure SetGradientColorButtons;
    procedure UpdateGradientEdit;
  public
    { Public declarations }
    class function InsertForm(const AGradient:TCustomTeeGradient;
                              const AParent:TFmxObject):TGradientEditor;
    class function Edit(const AOwner:TComponent; const AGradient:TCustomTeeGradient):Boolean;

    procedure RefreshGradient(const AGradient:TCustomTeeGradient);
  end;

implementation

{$R *.fmx}

uses
  {$IFDEF D19}
  FMX.Graphics,
  {$ENDIF}
  System.UIConsts, FMXTee.Editor.Stroke;

const
  TeeMaxSampleGradient=17;

procedure TGradientEditor.Button1Click(Sender: TObject);
var tmp : TColor;
begin
  tmp:=Gradient.StartColor;
  Gradient.StartColor:=Gradient.EndColor;
  Gradient.EndColor:=tmp;

  ColorButton1.Color:=Gradient.StartColor;
  ColorButton3.Color:=Gradient.EndColor;

  UpdateGradientEdit;
end;

procedure TGradientEditor.Button3Click(Sender: TObject);
begin
  Gradient.Assign(IBackup);
end;

procedure TGradientEditor.BDeleteColorClick(Sender: TObject);
begin
  Gradient.Colors.Delete(GradientEdit1.CurrentPoint);
  GradientEdit1.Gradient.Points.Delete(GradientEdit1.CurrentPoint);

  GradientEdit1.CurrentPoint:=-1;

  GradientEdit1.Repaint;

  GradientEdit1SelectPoint(Self);
end;

procedure TGradientEditor.CBDirectionChange(Sender: TObject);
begin
  Gradient.Direction:=TGradientDirection(CBDirection.ItemIndex);
end;

procedure TGradientEditor.CBUseMiddleChange(Sender: TObject);
begin
  if CBUseMiddle.IsChecked then
     Gradient.UseMiddleColor
  else
     Gradient.MidColor:=clNone;

  if Assigned(Gradient.Changed) then
     Gradient.Changed(Self);

  UpdateGradientEdit;
end;

procedure TGradientEditor.CBVisibleChange(Sender: TObject);
begin
  Gradient.Visible:=CBVisible.IsChecked;
end;

procedure TGradientEditor.SetDirectionGradients;
var t : Integer;
    tmp : TTeeCustomShape;
begin
  if Directions.Charts.Count>0 then
  begin
    for t:=0 to Directions.Charts.Count-1 do
    begin
      tmp:=Directions[t].BackWall;
      tmp.Gradient.Assign(Gradient);
      tmp.Gradient.Visible:=True;
      tmp.Gradient.Direction:=TGradientDirection(t);
    end;

    if CBDirection.ItemIndex>-1 then
       Directions.SelectChart(Directions[CBDirection.ItemIndex]);
  end;
end;

procedure TGradientEditor.UpdateGradientEdit;
var t : Integer;
begin
  IUpdating:=True;
  try
    GradientEdit1.Gradient.Points.Clear;

    for t:=0 to Gradient.Colors.Count-1 do
      with TGradientPoint(GradientEdit1.Gradient.Points.Add) do
      begin
        Color:=Self.Gradient.Colors[t].Color;
        Offset:=Self.Gradient.Colors[t].Offset;
      end;

    GradientEdit1.CurrentPoint:=0;
    GradientEdit1SelectPoint(Self);

    GradientEdit1.Repaint;
  finally
    IUpdating:=False;
  end;
end;

Function TGradientEditor.IsRectGradient:Boolean;
begin
  result:= (Gradient.Direction=gdTopBottom) or
           (Gradient.Direction=gdBottomTop) or
           (Gradient.Direction=gdLeftRight) or
           (Gradient.Direction=gdRightLeft);
end;

procedure TGradientEditor.SetGradientColorButtons;
begin
  IChangingButtons:=True;
  try
    ColorButton1.Color:=Gradient.StartColor;
    ColorButton2.Color:=Gradient.MidColor;
    ColorButton3.Color:=Gradient.EndColor;
  finally
    IChangingButtons:=False;
  end;
end;

procedure TGradientEditor.RefreshControls;
begin
  CBVisible.IsChecked:=Gradient.Visible;

  SetGradientColorButtons;

  CBUseMiddle.IsChecked:=Gradient.MidColor<>clNone;
  CBDirection.ItemIndex:=Ord(Gradient.Direction);

  TBX.Value:=Gradient.RadialX;
  TBY.Value:=Gradient.RadialY;

  {
    UDAngle.Position:=Angle;
    TBAngle.Position:=Angle;

    CBSameAsEnd.Checked:=StartIsEnd;
    BStart.Enabled:=not CBSameAsEnd.Checked;
  }

  UpdateGradientEdit;

  SetDirectionGradients;
  //SetPreview;
end;

procedure TGradientEditor.RefreshGradient(const AGradient: TCustomTeeGradient);
begin
  Gradient:=AGradient;
  IBackup.Assign(Gradient);

  RefreshControls;
end;

procedure TGradientEditor.ApplyChart(const Chart:TGalleryChart);
begin
  with Chart do
  begin
    Title.Text.Clear;
    View3D:=False;
    Axes.Visible:=False;
    BackWall.Gradient.Visible:=True;
    BackWall.Transparent:=False;
    MarginLeft:=16;
    MarginTop:=22;
    MarginRight:=16;
    MarginBottom:=22;
  end;
end;

procedure TGradientEditor.TabControl1Change(Sender: TObject);

  procedure AddDirectionGallery;
  var t : TGradientDirection;
      tmp : TGalleryChart;
  begin
    //Directions.DisplaySub:=False;
    Directions.NumRows:=2;
    Directions.NumCols:=5;

    for t:=Low(TGradientDirection) to High(TGradientDirection) do
    begin
      tmp:=TGalleryChart.Create(Directions);
      Directions.Charts.Add(tmp);
      tmp.Parent:=Directions;

      //tmp.Hint:=CBDirection.Items[Ord(t)];
      //tmp.ShowHint:=True;

      Directions.FinishAdd(tmp);
      tmp.OnEnter:=nil;

      ApplyChart(tmp);

      tmp.BackWall.Gradient.Direction:=TGradientDirection(t);
    end;

    Directions.ResizeCharts;

    TeeTranslateControl(Directions);

    SetDirectionGradients;
    SelectDirection;
  end;

  Procedure AddGallery;
  var t : Integer;
      tmp : TGalleryChart;
  begin
    Gallery.DisplaySub:=False;
    Gallery.NumRows:=4;
    Gallery.NumCols:=4;

    for t:=0 to TeeMaxSampleGradient do
    begin
      tmp:=TGalleryChart.Create(Self);
      Gallery.Charts.Add(tmp);
      tmp.Parent:=Gallery;
      Gallery.FinishAdd(tmp);
      tmp.OnEnter:=nil;
      ApplyChart(tmp);
      DefaultGradient(tmp.BackWall.Gradient,True,t);
    end;

    Gallery.ResizeCharts;
  end;

begin
  if TabControl1.ActiveTab=TabDirections then
  begin
    if Directions.Charts.Count=0 then
       AddDirectionGallery;
  end
  else
  if TabControl1.ActiveTab=TabGallery then
  begin
    if Gallery.Charts.Count=0 then
       AddGallery;
  end;
end;

class procedure TGradientEditor.DefaultGradient(const Gradient:TCustomTeeGradient;
                                         IsRectGradient:Boolean; Index:Integer);

  Procedure DefaultGradient(const AEnd,AMid,AStart:TColor;
                            const ADirection:TGradientDirection=gdTopBottom;
                            ABalance:Integer=50);
  begin
    Gradient.ResetColors;

    Gradient.StartColor:=AStart;
    Gradient.MidColor:=AMid;
    Gradient.EndColor:=AEnd;

    if (AMid<>clNone) and (not IsRectGradient) then
       Gradient.Direction:=gdTopBottom;

    Gradient.Direction:=ADirection;
    Gradient.Balance:=ABalance;
  end;

begin
  case Index of
    0: DefaultGradient($FFD2DFDD,$FF970AF5,$FFF4800B);
    1: DefaultGradient(claSilver,claWhite,clDkGray);
    2: DefaultGradient(claRed,clNone,claYellow);
    3: DefaultGradient(claBlack,$FF8080FF,claBlack,gdBottomTop,74);
    4: DefaultGradient($FFD56A00,claWhite,$FFC46200,gdBottomTop,4);
    5: DefaultGradient($FF80FFFF,$FF8080FF,claNavy,gdBottomTop,55);
    6: DefaultGradient(claWhite,$FF400080,claBlack,gdTopBottom,40);
    7: DefaultGradient($FF2003A5,$FF400080,claWhite,gdFromTopLeft);
    8: DefaultGradient($FF029915,claSilver,$FF86F9D9,gdTopBottom);
    9: DefaultGradient($FF8138BD,$FF7A6087,$FF85EB52,gdTopBottom);
   10: DefaultGradient($FF065AB8,$FFAFD8A7,$FF421000,gdTopBottom);
   11: DefaultGradient($FFE81EA1,$FF55830F,$FF1A01DD,gdTopBottom);
   12: DefaultGradient($FF897424,$FFF69194,$FF9D8BFD);
   13: DefaultGradient($FF4B419A,$FFF32F73,$FF61D720);
   14: DefaultGradient($FF1C034C,$FFE1CBBA,$FFF91E29);
   15: DefaultGradient($FFB75F26,$FF8571CE,$FF574460);
   16: DefaultGradient($FFEAF3FF,$FFE9E6E0,$FFFFDBCE);
   TeeMaxSampleGradient: DefaultGradient(claRed,claYellow,claGreen);
  end;
end;

procedure TGradientEditor.SelectDirection;
begin
  if Assigned(Gradient) then
     if Directions.Charts.Count>0 then
        Directions.SelectChart(Directions[Ord(Gradient.Direction)]);
end;

procedure TGradientEditor.TBXChange(Sender: TObject);
begin
  Gradient.RadialX:=Round(TBX.Value);
end;

procedure TGradientEditor.TBYChange(Sender: TObject);
begin
  Gradient.RadialY:=Round(TBY.Value);
end;

procedure TGradientEditor.ColorButton1Change(Sender: TObject);
begin
  if not IChangingButtons then
  begin
    Gradient.StartColor:=ColorButton1.Color;
    UpdateGradientEdit;
  end;
end;

procedure TGradientEditor.ColorButton2Change(Sender: TObject);
begin
  if not IChangingButtons then
  begin
    Gradient.MidColor:=ColorButton2.Color;
    UpdateGradientEdit;
  end;
end;

procedure TGradientEditor.ColorButton3Change(Sender: TObject);
begin
  if not IChangingButtons then
  begin
    Gradient.EndColor:=ColorButton3.Color;
    UpdateGradientEdit;
  end;
end;

procedure TGradientEditor.CurrentColorChange(Sender: TObject);
var tmp : TColor;
begin
  if not IChangingColor then
  begin
    tmp:=CurrentColor.Color;

    Gradient.Colors[GradientEdit1.CurrentPoint].Color:=tmp;

    if Assigned(Gradient.Changed) then
       Gradient.Changed(Self);

    GradientEdit1.Gradient.Points[GradientEdit1.CurrentPoint].Color:=tmp;
    GradientEdit1.Repaint;

    SetGradientColorButtons;
  end;
end;

class function TGradientEditor.InsertForm(const AGradient: TCustomTeeGradient;
    const AParent: TFmxObject): TGradientEditor;
begin
  result:=TGradientEditor.Create(AParent.Owner);
  result.RefreshGradient(AGradient);
  EmbeddForm(result,AParent);
end;

procedure TGradientEditor.DirectionsChangeChart(Sender: TObject);
var tmp : Integer;
begin
  tmp:=Directions.Charts.IndexOf(Directions.SelectedChart);
  Gradient.Direction:=TGradientDirection(tmp);
  CBDirection.ItemIndex:=tmp;

  IModified:=True;
  //CheckDirection;
end;

class function TGradientEditor.Edit(const AOwner: TComponent;
  const AGradient: TCustomTeeGradient): Boolean;
begin
  with TGradientEditor.Create(AOwner) do
  try
    PanelButtons.Visible:=True;
    RefreshGradient(AGradient);
    result:=ShowModal=mrOk;
  finally
    Free;
  end;
end;

procedure TGradientEditor.FormCreate(Sender: TObject);
begin
  IBackup:=TCustomTeeGradient.Create(nil);
  TabControl1.ActiveTab:=TabItem1;
end;

procedure TGradientEditor.FormDestroy(Sender: TObject);
begin
  IBackup.Free;
end;

procedure TGradientEditor.GalleryChangeChart(Sender: TObject);
begin
  if Gallery.SelectedChart<>nil then
  begin
    DefaultGradient(Gradient, IsRectGradient,
                    Gallery.Charts.IndexOf(Gallery.SelectedChart));

    RefreshControls;
  end;
end;

procedure TGradientEditor.GradientEdit1Change(Sender: TObject);
var t : Integer;
    P : TGradientPoints;
begin
  if not IUpdating then
  begin
    Gradient.Colors.Clear;

    P:=GradientEdit1.Gradient.Points;

    for t := 0 to P.Count-1 do
        Gradient.Colors.Add(P[t].Offset,P[t].Color);
  end;
end;

procedure TGradientEditor.GradientEdit1SelectPoint(Sender: TObject);
var tmp : Integer;
begin
  tmp:=GradientEdit1.CurrentPoint;

  CurrentColor.Enabled:=tmp<>-1;

  IChangingColor:=True;
  try
    if tmp=-1 then
       CurrentColor.Color:=TAlphaColors.Silver
    else
       CurrentColor.Color:=GradientEdit1.Gradient.Points[tmp].Color;
  finally
    IChangingColor:=False;
  end;

  BDeleteColor.Enabled:=CurrentColor.Enabled;
end;

end.
