// ------------------------------------------------------------------------------
// DPF.iOS.UILabel 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.UILabel;

interface

{$I DPF.iOS.Defs.inc}

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

  System.TypInfo,
  DPF.iOS.BaseControl,
  DPF.iOS.UIFont,
{$IFDEF IOS}
  Macapi.ObjectiveC,
  Macapi.ObjCRuntime,
  IOSapi.Foundation,
  IOSapi.Uikit,
  IOSapi.CoreGraphics,
  DPF.iOS.Common,
{$ELSE}
  // Added by Fenistil
  DPF.iOS.DesignTime,
{$ENDIF}
{$IFDEF DELPHIXE5} FMX.Graphics, {$ENDIF}FMX.Types;

type

  TDPFLabel = class;

{$IFDEF IOS}

  LabelGestureRecognizer = interface( IObjectiveC )
    ['{BBE3DCF5-1492-4DDC-A413-C87E78273457}']
    procedure handleSingleTap( gr: UITapGestureRecognizer ); cdecl;
  end;

  TDPFLabelGestureRecognizerDelegate = class( TOCLocal, LabelGestureRecognizer )
  private
    FDPFLabel: TDPFLabel;
  public
    constructor Create( ADPFLabel: TDPFLabel );
    procedure handleSingleTap( gr: UITapGestureRecognizer ); cdecl;
  end;
{$ENDIF}

  // ------------------------------------------------------------------------------
  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFLabel = class( TDPFiOSBaseControl )
  private
{$IFDEF IOS}
    FUILabel                          : UILabel;
    FTapGestureRecognizer             : UITapGestureRecognizer;
    FDPFLabelGestureRecognizerDelegate: TDPFLabelGestureRecognizerDelegate;
{$ENDIF}
    FText                     : string;
    FTextColor                : TAlphaColor;
    FLineBreak                : TDPFLineBreak;
    FBackgroundColor          : TAlphaColor;
    FNumberOfLines            : Integer;
    FTextAlignment            : TDPFTextAlignment;
    FAdjustsFontSizeToFitWidth: Boolean;
    FBackgroundImage          : string;
    FOnClick                  : TDPFOnClicked;
    FNumberOfTouchesRequired  : Byte;
    FNumberOfTapsRequired     : Byte;
    FDelaysTouchesBegan       : Boolean;
    FDelaysTouchesEnded       : Boolean;
    FAutoSizeToFit            : Boolean;
    procedure SetText( const Value: string );
    procedure SetTextColor( const Value: TAlphaColor );
    procedure SetBackgroundColor( const Value: TAlphaColor );
    procedure SetTextAlignment( const Value: TDPFTextAlignment );
    procedure SetBackgroundImage( const Value: string );
    procedure SetLineBreak( const Value: TDPFLineBreak );
    procedure SetNumberOfLines( const Value: Integer );
    procedure SetOnClick( const Value: TDPFOnClicked );

  protected
    procedure Resize; override;
    procedure Move; override;
    procedure FontOnChanged( Sender: TObject );
  public
{$IFDEF IOS}
    procedure Loaded; override;
{$ELSE}
    procedure Paint; override;
{$ENDIF}
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
    procedure RefreshNeeded; override;
    function sizeToFit: DPFNSize;
  published
    property LineBreak                : TDPFLineBreak read FLineBreak write SetLineBreak default lbCharacterWrap;
    property Text                     : string read FText write SetText;
    property TextColor                : TAlphaColor read FTextColor write SetTextColor default TAlphaColors.Black;
    property BackgroundColor          : TAlphaColor read FBackgroundColor write SetBackgroundColor default TAlphaColors.Null;
    property BackgroundImage          : string read FBackgroundImage write SetBackgroundImage;
    property NumberOfLines            : Integer read FNumberOfLines write SetNumberOfLines default 1;
    property TextAlignment            : TDPFTextAlignment read FTextAlignment write SetTextAlignment default taLeft;
    property AdjustsFontSizeToFitWidth: Boolean read FAdjustsFontSizeToFitWidth write FAdjustsFontSizeToFitWidth default False;
    property AutoSizeToFit            : Boolean read FAutoSizeToFit write FAutoSizeToFit default False;

    property NumberOfTapsRequired   : Byte read FNumberOfTapsRequired write FNumberOfTapsRequired default 1;
    property NumberOfTouchesRequired: Byte read FNumberOfTouchesRequired write FNumberOfTouchesRequired default 1;

    property DelaysTouchesBegan: Boolean read FDelaysTouchesBegan write FDelaysTouchesBegan default true;
    property DelaysTouchesEnded: Boolean read FDelaysTouchesEnded write FDelaysTouchesEnded default false;

    property OnClick: TDPFOnClicked read FOnClick write SetOnClick;

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

  // ------------------------------------------------------------------------------
implementation

// ------------------------------------------------------------------------------
{ TDPFLabel }
// ------------------------------------------------------------------------------
constructor TDPFLabel.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
  ControlCaption             := 'Label';
  FLineBreak                 := lbCharacterWrap;
  FTextColor                 := TAlphaColors.Black;
  FBackgroundColor           := TAlphaColors.Null;
  FNumberOfLines             := 1;
  FTextAlignment             := taLeft;
  FAdjustsFontSizeToFitWidth := False;
  FNumberOfTapsRequired      := 1;
  FNumberOfTouchesRequired   := 1;
  FDelaysTouchesBegan        := true;
  DelaysTouchesEnded         := false;
  Font.OnChanged             := FontOnChanged;
  ClipsToBounds              := false;
  FAutoSizeToFit             := False;

{$IFDEF IOS}
  FUILabel   := TUILabel.Create;
  FUIControl := FUILabel;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
destructor TDPFLabel.Destroy;
begin
{$IFDEF IOS}
  if assigned( FTapGestureRecognizer ) then
    FUILabel.removeGestureRecognizer( FTapGestureRecognizer );

  if assigned( FDPFLabelGestureRecognizerDelegate ) then
    FDPFLabelGestureRecognizerDelegate.DisposeOf;

{$ENDIF}
  inherited;
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.FontOnChanged( Sender: TObject );
begin
  inherited;
{$IFDEF IOS}
  if FUILabel <> nil then
  begin
    FUILabel.setFont( Font._UIFont );
  end;
{$ENDIF}
end;

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

// ------------------------------------------------------------------------------
function TDPFLabel.sizeToFit: DPFNSize;
begin
{$IFDEF IOS}
  FUILabel.sizeToFit;
  result.width  := FUILabel.frame.size.width;
  result.height := FUILabel.frame.size.height;
{$ENDIF}
end;

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

procedure TDPFLabel.Loaded;
begin
  Resize;
  SetText( FText );
  SetLineBreak( FLineBreak );

  SetTextColor( FTextColor );

  FUILabel.setEnabled( Enabled );
  FUILabel.setHidden( not Visible );
  setNumberOfLines( FNumberOfLines );
  SetTextAlignment( FTextAlignment );
  FUILabel.setAdjustsFontSizeToFitWidth( FAdjustsFontSizeToFitWidth );
  FUILabel.setFont( Font._UIFont );

  SetBackgroundColor( FBackgroundColor );
  SetBackgroundImage( FBackgroundImage );

  AddSubView( Self, ParentControl );

  // ----------------------------
  // Important
  inherited;

  // ----------------------------
  // Important: Must be after AddSubView
  if FAutoSizeToFit then
    sizeToFit;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFLabel.RefreshNeeded;
begin
  inherited;
  Loaded;
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.Resize;
begin
  inherited;
  (*
    {$IFDEF IOS}
    if FUILabel <> nil then
    begin
    FUILabel.SetFrame( CGRectMake( Position.X, Position.Y, Width, Height ) )
    end;
    {$ENDIF}
  *)
end;

// ------------------------------------------------------------------------------
{$IFNDEF IOS}

procedure TDPFLabel.Paint;
var
  Caption    : string;
  CaptionRect: TRectF;

begin
  // Added by Fenistil
  Canvas.BeginScene;
  Canvas.Fill.Kind := TBrushKind.bkSolid;
  if BackgroundColor <> TAlphaColors.Null then
  begin
    Canvas.Fill.Color := BackgroundColor;
    Canvas.FillRect( ClipRect, 0, 0, AllCorners, Alpha, TCornerType.ctInnerRound );
  end;
  if ( Text = '' ) and ( csDesigning in ComponentState ) then
  begin
    Caption           := name;
    Canvas.Fill.Color := TAlphaColors.Gray;
  end
  else
  begin
    Caption           := Text;
    Canvas.Fill.Color := TextColor;
  end;
  Canvas.Font.Size   := Font.FontSize;
  Canvas.Font.Style  := [TFontStyle.fsBold];
  Canvas.Font.Family := GetDeviceFontName( Font.FontName );
  CaptionRect        := ClipRect;
  PaintCaption( Self, Caption, CaptionRect, LineBreak, NumberOfLines, CTextAlign[FTextAlignment] );
  Canvas.EndScene;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetBackgroundColor( const Value: TAlphaColor );
begin
  FBackgroundColor := Value;
{$IFDEF IOS}
  if Assigned( FUILabel ) and ( FBackgroundImage = '' ) then
  begin
    if FBackgroundColor <> TAlphaColors.Null then
      FUILabel.SetBackgroundColor( TColorToUIColor( FBackgroundColor ) )
    else
      FUILabel.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetBackgroundImage( const Value: string );
{$IFDEF IOS}
var
  Image2: UIImage;
  Image1: UIImage;
  NData : NSData;
{$IFNDEF IOSDEVICE}
  transform: CGAffineTransform;
  context  : CGContextRef;
{$ENDIF}
{$ENDIF}
begin
  FBackgroundImage := Value;
{$IFDEF IOS}
  if Assigned( FUILabel ) then
  begin
    if FBackgroundImage <> '' then
    begin
      // Image2 := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FBackgroundImage ) ) );
      Image1 := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( FBackgroundImage ) ) );

      if not Assigned( Image1 ) or Assigned( Image1.CGImage ) or Assigned( Image1.CIImage ) then
      begin
        NData  := TNSData.Wrap( TNSData.OCClass.dataWithContentsOfURL( TNSURL.Wrap( TNSURL.OCClass.URLWithString( NSStr( FBackgroundImage ) ) ) ) );
        Image1 := TUIImage.Wrap( TUIImage.OCClass.imageWithData( NData ) );
      end;

      UIGraphicsBeginImageContext( FUILabel.frame.size );
{$IFNDEF IOSDEVICE}
      // ----------------------------------------------------------
      // Mirror on Simulator
      context   := UIGraphicsGetCurrentContext( );
      transform := CGAffineTransformMakeTranslation( 0.0, Height );
      transform := CGAffineTransformScale( transform, 1.0, -1.0 );
      CGContextConcatCTM( context, transform );
{$ENDIF}
      Image1.drawInRect( FUILabel.bounds );
      Image2 := TUIImage.Wrap( UIGraphicsGetImageFromCurrentImageContext( ) );
      UIGraphicsEndImageContext( );

      FUILabel.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.colorWithPatternImage( Image2 ) ) );
    end
    else
      SetBackgroundColor( FBackgroundColor );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetLineBreak( const Value: TDPFLineBreak );
begin
  FLineBreak := Value;
{$IFDEF IOS}
  if Assigned( FUILabel ) then
    FUILabel.SetLineBreakMode( Integer( FLineBreak ) );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetNumberOfLines( const Value: Integer );
begin
  FNumberOfLines := Value;
{$IFDEF IOS}
  if Assigned( FUILabel ) then
  begin
    FUILabel.setNumberOfLines( Value );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetOnClick( const Value: TDPFOnClicked );
begin
  FOnClick := Value;

{$IFDEF IOS}
  if assigned( FTapGestureRecognizer ) then
    FUILabel.removeGestureRecognizer( FTapGestureRecognizer );

  if assigned( FDPFLabelGestureRecognizerDelegate ) then
    FDPFLabelGestureRecognizerDelegate.DisposeOf;

  if Assigned( Value ) then
  begin
    FDPFLabelGestureRecognizerDelegate := TDPFLabelGestureRecognizerDelegate.Create( Self );
    FTapGestureRecognizer              := TUITapGestureRecognizer.Wrap( TUITapGestureRecognizer.Alloc.initWithTarget( FDPFLabelGestureRecognizerDelegate.GetObjectID, Sel_getUid( 'handleSingleTap:' ) ) );

    FTapGestureRecognizer.setNumberOfTapsRequired( FNumberOfTapsRequired );
    FTapGestureRecognizer.setNumberOfTouchesRequired( FNumberOfTouchesRequired );
    FTapGestureRecognizer.setDelaysTouchesBegan( FDelaysTouchesBegan );
    FTapGestureRecognizer.setDelaysTouchesEnded( FDelaysTouchesEnded );
    FUILabel.removeGestureRecognizer( FTapGestureRecognizer );
    FUILabel.addGestureRecognizer( FTapGestureRecognizer );
  end
  else
  begin
    if assigned( FTapGestureRecognizer ) then
      FUILabel.removeGestureRecognizer( FTapGestureRecognizer );

    if assigned( FDPFLabelGestureRecognizerDelegate ) then
      FDPFLabelGestureRecognizerDelegate.DisposeOf;
  end
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetText( const Value: string );
begin
  FText := Value;
{$IFDEF IOS}
  if Assigned( FUILabel ) then
  begin
    FUILabel.SetText( NSStr( Value ) );
  end;
  if FAutoSizeToFit then
    sizeToFit;

{$ELSE}
  ControlCaption := FText;
  Invalidate;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFLabel.SetTextAlignment( const Value: TDPFTextAlignment );
begin
  FTextAlignment := Value;
{$IFDEF IOS}
  if Assigned( FUILabel ) then
  begin
    FUILabel.SetTextAlignment( Integer( FTextAlignment ) );
  end;
{$ELSE}
  Invalidate;
{$ENDIF}
end;

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

{$IFDEF IOS}

// ------------------------------------------------------------------------------
{ TDPFLabelGestureRecognizerDelegate }
constructor TDPFLabelGestureRecognizerDelegate.Create( ADPFLabel: TDPFLabel );
begin
  inherited Create;
  FDPFLabel := ADPFLabel;
end;

// ------------------------------------------------------------------------------
procedure TDPFLabelGestureRecognizerDelegate.handleSingleTap( gr: UITapGestureRecognizer );
begin
  if Assigned( FDPFLabel.FOnClick ) then
    FDPFLabel.FOnClick( FDPFLabel );
end;
{$ENDIF}

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