// ------------------------------------------------------------------------------
// DPF.iOS.UISlider Component
//
// Dadeh Pardazane Faragir ( DPF ) Co.
//
// Web: http://www.dpfaragir.com
//
// Developed By: Babak Yaghoobi
//
// Email #1: yaghoobi@dpfaragir.com
// Email #2: b_yaghobi@yahoo.com
//
// ------------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ------------------------------------------------------------------------------
unit DPF.iOS.UISlider;

interface

{$I DPF.iOS.Defs.inc}

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  System.UITypes,
  System.Variants,

  System.TypInfo,
  DPF.iOS.BaseControl,
{$IFDEF IOS}
  Macapi.ObjectiveC,
  Macapi.ObjCRuntime,
  IOSapi.CocoaTypes,
  IOSapi.Foundation,
  IOSapi.Uikit,
  IOSapi.CoreGraphics,
  IOSapi.CoreImage,
  FMX.Platform.iOS,
  DPF.iOS.Common,
{$ELSE}
  // Added by Fenistil
  DPF.iOS.DesignTime,
{$ENDIF}
  FMX.Layouts, FMX.Memo,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type
  TDPFSlider = class;

  TDPFSliderKind         = ( skNormal, skCustom );
  TDPFSliderValueChanged = procedure( Sender: TObject; CurValue: Single ) of object;

{$IFDEF IOS}

  // ------------------------------------------------------------------------------
  DPFSliderDelegate = interface( NSObject )
    ['{4131A3E8-F16A-47DA-B840-494263C7677C}']
    procedure valueChanged; cdecl;
  end;

  // ------------------------------------------------------------------------------
  TDPFSliderDelegate = class( TOCLocal )
  private
    FDPFSlider: TDPFSlider;
  public
    OnChanged: TDPFSliderValueChanged;
    constructor Create( ADPFSlider: TDPFSlider );
    function GetObjectiveCClass: PTypeInfo; override;
    procedure valueChanged; cdecl;
  end;
{$ENDIF}

  // ------------------------------------------------------------------------------
  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFSlider = class( TDPFiOSBaseControl )
  private
{$IFDEF IOS}
    FUISlider      : UISlider;
    FSliderDelegate: TDPFSliderDelegate;
{$ENDIF}
    FBackgroundColor      : TAlphaColor;
    FMaximumValue         : Integer;
    FMinimumValue         : Integer;
    FValue                : Single;
    FContinuous           : Boolean;
    FMinimumTrackTintColor: TAlphaColor;
    FMaximumTrackTintColor: TAlphaColor;
    FAnimated             : Boolean;
    FOnChanged            : TDPFSliderValueChanged;
    FThumbTintColor       : TAlphaColor;
    FMinimumTrackImage    : string;
    FThumbImage           : string;
    FMaximumTrackImage    : string;
    FSliderKind           : TDPFSliderKind;
    procedure SetBackgroundColor( const Value: TAlphaColor );
    procedure SetMaximumValue( const cMaximumValue: Integer );
    procedure SetMinimumValue( const cMinimumValue: Integer );
    procedure SetValue( const cValue: Single );

  protected
    procedure Resize; override;
    procedure Move; override;
  public
{$IFDEF IOS}
    procedure Loaded; override;
{$ELSE}
    procedure Paint; override;
{$ENDIF}
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
  published
    property Animated       : Boolean read FAnimated write FAnimated default True;
    property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor default TAlphaColors.Null;

    property MaximumTrackTintColor: TAlphaColor read FMaximumTrackTintColor write FMaximumTrackTintColor default TAlphaColors.Red;
    property MinimumTrackTintColor: TAlphaColor read FMinimumTrackTintColor write FMinimumTrackTintColor default TAlphaColors.Blue;
    property ThumbTintColor       : TAlphaColor read FThumbTintColor write FThumbTintColor default TAlphaColors.Gray;

    property MaximumTrackImage: string read FMaximumTrackImage write FMaximumTrackImage;
    property MinimumTrackImage: string read FMinimumTrackImage write FMinimumTrackImage;
    property ThumbImage       : string read FThumbImage write FThumbImage;

    property MinimumValue: Integer read FMinimumValue write SetMinimumValue default 0;
    property MaximumValue: Integer read FMaximumValue write SetMaximumValue default 100;
    property Value       : Single read FValue write SetValue;
    property Continuous  : Boolean read FContinuous write FContinuous default True;
    property SliderKind  : TDPFSliderKind read FSliderKind write FSliderKind default skNormal;

    property OnChanged: TDPFSliderValueChanged read FOnChanged write FOnChanged;

    property UserInteraction;
    property ContentMode;
    property Alpha;
    property Align;
    property Position;
    property Width;
    property Height;
    property Enabled;
    property Visible;
  end;

implementation

// ------------------------------------------------------------------------------
{ TDPFSlider }
// ------------------------------------------------------------------------------
constructor TDPFSlider.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
  ControlCaption         := 'Slider';
  FBackgroundColor       := TAlphaColors.Null;
  FMaximumTrackTintColor := TAlphaColors.Red;
  FMinimumTrackTintColor := TAlphaColors.Blue;
  FThumbTintColor        := TAlphaColors.Gray;
  FMinimumValue          := 0;
  FMaximumValue          := 100;
  FValue                 := 50.0;
  FContinuous            := True;
  FAnimated              := True;
  FSliderKind            := skNormal;
{$IFDEF IOS}
  FSliderDelegate := TDPFSliderDelegate.Create( Self );
  FUISlider       := TUISlider.Create;
  FUIControl      := FUISlider;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
destructor TDPFSlider.Destroy;
begin
{$IFDEF IOS}
  FSliderDelegate.DisposeOf;
{$ENDIF}
  inherited;
end;

// ------------------------------------------------------------------------------
{$IFDEF IOS}

procedure TDPFSlider.Loaded;
var
  Image: UIImage;
begin

  FUISlider.setEnabled( Enabled );
  FUISlider.setHidden( not Visible );

  FUISlider.SetMinimumValue( FMinimumValue );
  FUISlider.SetMaximumValue( FMaximumValue );
  FUISlider.SetValue( FValue, FAnimated );
  FUISlider.setContinuous( FContinuous );

  if FSliderKind = skCustom then
  begin
    if FMaximumTrackImage <> '' then
    begin
      Image := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FMaximumTrackImage ) ) );
      FUISlider.setMaximumTrackImage( Image, UIControlStateNormal );
      Image.release;
      Image := nil;
    end
    else
    begin
      if ( FMaximumTrackTintColor = TAlphaColors.Null ) then
      begin
        if Assigned( FUISlider.MaximumTrackTintColor ) then
          FUISlider.setMaximumTrackTintColor( nil )
      end
      else
        FUISlider.setMaximumTrackTintColor( TColorToUIColor( FMaximumTrackTintColor ) );
    end;

    if FMinimumTrackImage <> '' then
    begin
      Image := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FMinimumTrackImage ) ) );
      FUISlider.setMinimumTrackImage( Image, UIControlStateNormal );
      Image.release;
      Image := nil;
    end
    else
    begin
      if ( FMinimumTrackTintColor = TAlphaColors.Null ) then
      begin
        if Assigned( FUISlider.MinimumTrackTintColor ) then
          FUISlider.setMinimumTrackTintColor( nil )
      end
      else
        FUISlider.setMinimumTrackTintColor( TColorToUIColor( FMinimumTrackTintColor ) );
    end;

    if FThumbImage <> '' then
    begin
      Image := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FThumbImage ) ) );
      FUISlider.setThumbImage( Image, UIControlStateNormal );
      Image.release;
      Image := nil;
    end
    else
    begin
      if ( FThumbTintColor = TAlphaColors.Null ) then
      begin
        if Assigned( FUISlider.ThumbTintColor ) then
          FUISlider.setThumbTintColor( nil )
      end
      else
        FUISlider.setThumbTintColor( TColorToUIColor( FThumbTintColor ) );
    end;

    if FBackgroundColor = TAlphaColors.Null then
      FUISlider.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) )
    else
      FUISlider.SetBackgroundColor( TColorToUIColor( FBackgroundColor ) );
  end;
  FUISlider.AddTarget( FSliderDelegate.GetObjectID, // target
    Sel_getUid( 'valueChanged' ),                   // action
    UIControlEventValueChanged );                   // event

  AddSubView( Self, ParentControl );
  // ----------------------------
  // Important
  inherited;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFSlider.Resize;
begin
  inherited;
{$IFDEF IOS}
  //if FUISlider <> nil then FUISlider.SetFrame( CGRectMake( Position.X, Position.Y, Width, Height ) );
{$ELSE}
  // Added by Fenistil
  Height := iOS_GUI_Bitmaps.Slider.Tab.Height;
  Invalidate;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFSlider.Move;
begin
  Resize;
end;

{$IFNDEF IOS}

// ------------------------------------------------------------------------------
procedure TDPFSlider.Paint;
var
  m, v, px: double;
  pos, top: integer;
begin
  // Added by Fenistil
  Canvas.BeginScene;
  m := MaximumValue - MinimumValue;
  if m <= 0 then
    m := 1;
  v   := Value - MinimumValue;
  px  := Width - iOS_GUI_Bitmaps.Slider.Tab.Width;
  pos := Round( v / m * px );
  top := Round( iOS_GUI_Bitmaps.Slider.Tab.Height / 2 - iOS_GUI_Bitmaps.Slider.BarOff.Height / 2 );
  // Blue bar ending
  Canvas.DrawBitmap( iOS_GUI_Bitmaps.Slider.BarOn, RectF( 0, 0, 4, iOS_GUI_Bitmaps.Slider.BarOn.Height ), RectF( 3, top, 7, top + iOS_GUI_Bitmaps.Slider.BarOn.Height ), 1 );
  // Blue bar
  if pos >= 7 then
    Canvas.DrawBitmap( iOS_GUI_Bitmaps.Slider.BarOn, RectF( 5, 0, 6, iOS_GUI_Bitmaps.Slider.BarOn.Height ), RectF( 7, top, pos + iOS_GUI_Bitmaps.Slider.Tab.Width / 2, top + iOS_GUI_Bitmaps.Slider.BarOn.Height ), 1 );
  // White bar
  Canvas.DrawBitmap( iOS_GUI_Bitmaps.Slider.BarOff, RectF( iOS_GUI_Bitmaps.Slider.BarOff.Width - 6, 0, iOS_GUI_Bitmaps.Slider.BarOff.Width - 5, iOS_GUI_Bitmaps.Slider.BarOff.Height ), RectF( pos + iOS_GUI_Bitmaps.Slider.Tab.Width / 2, top, Width - 7, top + iOS_GUI_Bitmaps.Slider.BarOn.Height ), 1 );
  // White bar ending
  Canvas.DrawBitmap( iOS_GUI_Bitmaps.Slider.BarOff, RectF( iOS_GUI_Bitmaps.Slider.BarOff.Width - 4, 0, iOS_GUI_Bitmaps.Slider.BarOff.Width, iOS_GUI_Bitmaps.Slider.BarOff.Height ), RectF( Width - 7, top, Width - 3, top + iOS_GUI_Bitmaps.Slider.BarOn.Height ), 1 );
  // Tab
  Canvas.DrawBitmap( iOS_GUI_Bitmaps.Slider.Tab, RectF( 0, 0, iOS_GUI_Bitmaps.Slider.Tab.Width, iOS_GUI_Bitmaps.Slider.Tab.Height ), RectF( pos, 0, pos + iOS_GUI_Bitmaps.Slider.Tab.Width, iOS_GUI_Bitmaps.Slider.Tab.Height ), 1 );
  Canvas.EndScene;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFSlider.SetBackgroundColor( const Value: TAlphaColor );
begin
  FBackgroundColor := Value;
{$IFDEF IOS}
  if FUISlider <> nil then
  begin
    if FBackgroundColor <> TAlphaColors.Null then
      FUISlider.SetBackgroundColor( TColorToUIColor( FBackgroundColor ) )
    else
      FUISlider.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFSlider.SetMaximumValue( const cMaximumValue: Integer );
begin
  FMaximumValue := cMaximumValue;
  if Value > FMaximumValue then
    Value := FMaximumValue;
  if FMinimumValue > FMaximumValue then 
    FMinimumValue := FMaximumValue;
{$IFDEF IOS}
  if FUISlider <> nil then
  begin
    FUISlider.SetMinimumValue( FMinimumValue );
    FUISlider.SetMaximumValue( FMaximumValue );
    FUISlider.SetValue( FValue, FAnimated );
  end;
{$ELSE}
  // Added by Fenistil
  Invalidate;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFSlider.SetMinimumValue( const cMinimumValue: Integer );
begin
  FMinimumValue := cMinimumValue;
  if Value < FMinimumValue then
    Value := FMinimumValue;
  if FMaximumValue < FMinimumValue then
    FMaximumValue := FMinimumValue;
{$IFDEF IOS}
  if FUISlider <> nil then
  begin
    FUISlider.SetMinimumValue( FMinimumValue );
    FUISlider.SetMaximumValue( FMaximumValue );
    FUISlider.SetValue( FValue, FAnimated );
  end;
{$ELSE}
  // Added by Fenistil
  Invalidate;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFSlider.SetValue( const cValue: Single );
begin
  if cValue < FMinimumValue then
    FValue := FMinimumValue
  else if cValue > FMaximumValue then
    FValue := FMaximumValue
  else
    FValue := cValue;

{$IFDEF IOS}
  if FUISlider <> nil then
  begin
    FUISlider.SetMinimumValue( FMinimumValue );
    FUISlider.SetMaximumValue( FMaximumValue );
    FUISlider.SetValue( FValue, FAnimated );
  end;
{$ELSE}
  // Added by Fenistil
  Invalidate;
{$ENDIF}
end;

{$IFDEF IOS}
// ------------------------------------------------------------------------------
{ TDPFSliderDelegate }

constructor TDPFSliderDelegate.Create( ADPFSlider: TDPFSlider );
begin
  inherited Create;
  FDPFSlider := ADPFSlider;
end;

// ------------------------------------------------------------------------------
function TDPFSliderDelegate.GetObjectiveCClass: PTypeInfo;
begin
  Result := TypeInfo( DPFSliderDelegate );
end;

// ------------------------------------------------------------------------------
procedure TDPFSliderDelegate.valueChanged; cdecl;
begin
  FDPFSlider.FValue := FDPFSlider.FUISlider.Value;
  if Assigned( FDPFSlider.OnChanged ) then
  begin
    FDPFSlider.OnChanged( FDPFSlider, FDPFSlider.FValue );
  end;
end;

{$ENDIF}

// ------------------------------------------------------------------------------
end.
