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

interface

{$I DPF.iOS.Defs.inc}

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

  System.TypInfo,
  DPF.iOS.UIScrollView,
  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,
{$ENDIF}
  FMX.Layouts, FMX.Memo,
{$IFDEF DELPHIXE5} FMX.Graphics, {$ENDIF}FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type
{$M+}
  TDPFTextView = class;

{$IFDEF IOS}

  UITextViewClass = interface( UIScrollViewClass )
    ['{94A993C8-9DEF-407F-B2AA-B1CC8C898511}']
  end;

  UITextView = interface( UIScrollView )
    ['{BCC358C2-93EB-430E-A25C-1C9E68241B21}']
    function dataDetectorTypes: UIDataDetectorTypes; cdecl;
    function delegate: Pointer; cdecl;
    function font: UIFont; cdecl;
    function hasText: Boolean; cdecl;
    function inputAccessoryView: UIView; cdecl;
    function inputView: UIView; cdecl;
    function isEditable: Boolean; cdecl;
    procedure scrollRangeToVisible( range: NSRange ); cdecl;
    function selectedRange: NSRange; cdecl;
    procedure setDataDetectorTypes( dataDetectorTypes: UIDataDetectorTypes ); cdecl;
    procedure setDelegate( delegate: Pointer ); cdecl;
    procedure setEditable( editable: Boolean ); cdecl;
    procedure setFont( font: UIFont ); cdecl;
    procedure setInputAccessoryView( inputAccessoryView: UIView ); cdecl;
    procedure setInputView( inputView: UIView ); cdecl;
    procedure setSelectedRange( selectedRange: NSRange ); cdecl;
    procedure setText( text: NSString ); cdecl;
    procedure setTextAlignment( textAlignment: UITextAlignment ); cdecl;
    procedure setTextColor( textColor: UIColor ); cdecl;
    function text: NSString; cdecl;
    function textAlignment: UITextAlignment; cdecl;
    function textColor: UIColor; cdecl;

    function keyboardType: UIKeyboardType; cdecl; // Added by me
    procedure setKeyboardType( keyboardType: UIKeyboardType ); cdecl; // Added by me

    function autocapitalizationType: UITextAutocapitalizationType; cdecl; // Added by me
    procedure setAutocapitalizationType( autocapitalizationType: UITextAutocapitalizationType ); cdecl; // Added by me

    function autocorrectionType: UITextAutocorrectionType; cdecl; // Added by me
    procedure setAutocorrectionType( autocorrectionType: UITextAutocorrectionType ); cdecl; // Added by me

    function spellCheckingType: UITextSpellCheckingType; cdecl; // Added by me
    procedure setSpellCheckingType( spellCheckingType: UITextSpellCheckingType ); cdecl; // Added by me

    function enablesReturnKeyAutomatically: Boolean; cdecl; // Added by me
    procedure setEnablesReturnKeyAutomatically( enablesReturnKeyAutomatically: Boolean ); cdecl; // Added by me

    function keyboardAppearance: UIKeyboardAppearance; cdecl; // Added by me
    procedure setKeyboardAppearance( keyboardAppearance: UIKeyboardAppearance ); cdecl; // Added by me

    function returnKeyType: UIReturnKeyType; cdecl; // Added by me
    procedure setReturnKeyType( returnKeyType: UIReturnKeyType ); cdecl; // Added by me

    function secureTextEntry: Boolean; cdecl; // Added by me
    procedure setSecureTextEntry( secureTextEntry: Boolean ); cdecl; // Added by me

  end;

  TUITextView = class( TOCGenericImport<UITextViewClass, UITextView> )
  end;

  DPFITextViewDelegate = interface( UIScrollViewDelegate )
    ['{49E0F81F-8450-43C1-945A-AA870573F1FF}']
    procedure textViewDidChange( textView: UITextView ); cdecl;
    function textView( textView: UITextView; shouldChangeTextInRange: NSRange; replacementText: NSString ): Boolean; cdecl;

    // procedure textViewDidBeginEditing( textView: UITextView ); cdecl;
    // procedure textViewDidChangeSelection( textView: UITextView ); cdecl;
    // procedure textViewDidEndEditing( textView: UITextView ); cdecl;
    // function textViewShouldBeginEditing( textView: UITextView ): Boolean; cdecl;
    // function textViewShouldEndEditing( textView: UITextView ): Boolean; cdecl;
  end;

  TDPFTextViewDelegate = class( TOCLocal, DPFITextViewDelegate )
  private
    FDPFTextView: TDPFTextView;
  public
    constructor Create( ADPFTextView: TDPFTextView );
    // function GetObjectiveCClass: PTypeInfo; override;

    procedure textViewDidChange( textView: UITextView ); cdecl;
    function textView( textView: UITextView; shouldChangeTextInRange: NSRange; replacementText: NSString ): Boolean; cdecl;

    procedure scrollViewWillBeginDragging( scrollView: UIScrollView ); cdecl;

    procedure scrollViewDidScroll( scrollView: UIScrollView ); cdecl;
    procedure scrollViewDidEndDecelerating( scrollView: UIScrollView ); cdecl;

    // procedure textViewDidBeginEditing( textView: UITextView ); cdecl;
    // procedure textViewDidChangeSelection( textView: UITextView ); cdecl;
    // procedure textViewDidEndEditing( textView: UITextView ); cdecl;
    // function textViewShouldBeginEditing( textView: UITextView ): Boolean; cdecl;
    // function textViewShouldEndEditing( textView: UITextView ): Boolean; cdecl;
  end;
{$ENDIF}

  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFTextView = class( TDPFiOSBaseControl )
  private
{$IFDEF IOS}
    FUITextView         : UITextView;
    FDPFTextViewDelegate: TDPFTextViewDelegate;
{$ENDIF}
    FText                         : string;
    FTextColor                    : TAlphaColor;
    FBackgroundColor              : TAlphaColor;
    FTextAlignment                : TDPFTextAlignment;
    FScrollEnabled                : Boolean;
    FEditable                     : Boolean;
    FSsecureTextEntry             : Boolean;
    FKeyboardAppearance           : TDPFKeyboardAppearance;
    FEnablesReturnKeyAutomatically: Boolean;
    FSpellCheckingType            : TDPFTextSpellCheckingType;
    FAutocorrectionType           : TDPFTextAutocorrectionType;
    FAutocapitalizationType       : TDPFTextAutocapitalizationType;
    FReturnKey                    : TDPFReturnKey;
    FKeyboardType                 : TDPFKeyboardType;
    FOnTextChanged                : TNotifyEvent;
    FMaxlength                    : Integer;
    procedure SetText( const Value: string );
    procedure SetTextColor( const Value: TAlphaColor );
    procedure SetBackgroundColor( const Value: TAlphaColor );
    function GetText: string;
    procedure SetEditable( const Value: Boolean );
    procedure setKeyboardType( const Value: TDPFKeyboardType );

  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 TextAlignment  : TDPFTextAlignment read FTextAlignment write FTextAlignment default taLeft;
    property Text           : string read GetText write SetText;
    property TextColor      : TAlphaColor read FTextColor write SetTextColor default TAlphaColors.Black;
    property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor default TAlphaColors.White;
    property ScrollEnabled  : Boolean read FScrollEnabled write FScrollEnabled default True;
    property Editable       : Boolean read FEditable write SetEditable default True;
    property Maxlength      : Integer read FMaxlength write FMaxlength default 0;

    property AutocapitalizationType       : TDPFTextAutocapitalizationType read FAutocapitalizationType write FAutocapitalizationType default TDPFTextAutocapitalizationType.tapNone;
    property AutocorrectionType           : TDPFTextAutocorrectionType read FAutocorrectionType write FAutocorrectionType default TDPFTextAutocorrectionType.tatDefault;
    property SpellCheckingType            : TDPFTextSpellCheckingType read FSpellCheckingType write FSpellCheckingType default TDPFTextSpellCheckingType.tsctDefault;
    property EnablesReturnKeyAutomatically: Boolean read FEnablesReturnKeyAutomatically write FEnablesReturnKeyAutomatically default true;
    property KeyboardAppearance           : TDPFKeyboardAppearance read FKeyboardAppearance write FKeyboardAppearance default TDPFKeyboardAppearance.kaDefault;
    property ReturnKey                    : TDPFReturnKey read FReturnKey write FReturnKey default TDPFReturnKey.rkDefault;
    property SecureTextEntry              : Boolean read FSsecureTextEntry write FSsecureTextEntry default false;
    property KeyboardType                 : TDPFKeyboardType read FKeyboardType write setKeyboardType default ktDefault;
    property OnTextChanged                : TNotifyEvent read FOnTextChanged write FOnTextChanged;

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

implementation

// ------------------------------------------------------------------------------
constructor TDPFTextView.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
  ControlCaption   := 'Text View (Memo)';
  FMaxlength       := 0;
  FText            := '';
  FTextColor       := TAlphaColors.Black;
  FBackgroundColor := TAlphaColors.White;
  FTextAlignment   := taLeft;
  FVisible         := True;
  FEditable        := True;
  FScrollEnabled   := True;

  FAutocapitalizationType        := TDPFTextAutocapitalizationType.tapNone;
  FAutocapitalizationType        := TDPFTextAutocapitalizationType.tapNone;
  FAutocorrectionType            := TDPFTextAutocorrectionType.tatDefault;
  FSpellCheckingType             := TDPFTextSpellCheckingType.tsctDefault;
  FEnablesReturnKeyAutomatically := true;
  FKeyboardAppearance            := TDPFKeyboardAppearance.kaDefault;
  FReturnKey                     := TDPFReturnKey.rkDefault;
  FSsecureTextEntry              := false;

{$IFDEF IOS}
  FUITextView := TUITextView.Wrap( TUITextView.Alloc.init );
  FUIControl  := FUITextView;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
destructor TDPFTextView.Destroy;
begin
{$IFDEF IOS}
  if Assigned( FDPFTextViewDelegate ) then
    FDPFTextViewDelegate.DisposeOf;
{$ENDIF}
  inherited;
end;

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

procedure TDPFTextView.Loaded;
begin

  if not Assigned( FDPFTextViewDelegate ) then
  begin
    FDPFTextViewDelegate := TDPFTextViewDelegate.Create( Self );
  end;

  SetText( FText );

  FUITextView.SetFrame( CGRectMake( Position.X, Position.Y, Width, Height ) );
  FUITextView.SetTextColor( TColorToUIColor( FTextColor ) );

  SetBackgroundColor( FBackgroundColor );

  FUITextView.setTextAlignment( Integer( FTextAlignment ) );
  FUITextView.setHidden( not FVisible );
  FUITextView.setScrollEnabled( FScrollEnabled );
  FUITextView.setShowsHorizontalScrollIndicator( True );
  FUITextView.setShowsVerticalScrollIndicator( True );
  FUITextView.setFont( Font._UIFont );

  FUITextView.setKeyboardType( Integer( FKeyboardType ) );
  FUITextView.setAutocapitalizationType( Integer( FAutocapitalizationType ) );
  FUITextView.setAutocorrectionType( Integer( FAutocorrectionType ) );
  FUITextView.setSpellCheckingType( Integer( FSpellCheckingType ) );
  FUITextView.setEnablesReturnKeyAutomatically( FEnablesReturnKeyAutomatically );
  FUITextView.setKeyboardAppearance( Integer( FKeyboardAppearance ) );
  FUITextView.setReturnKeyType( Integer( FReturnKey ) );
  FUITextView.setSecureTextEntry( FSsecureTextEntry );

  if Assigned( FDPFTextViewDelegate ) then
    FUITextView.setDelegate( FDPFTextViewDelegate.GetObjectID );

  SetEditable( FEditable );

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

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

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

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

procedure TDPFTextView.Paint;
var
  Caption: string;

begin
  // Added by Fenistil
  Canvas.BeginScene;
  Canvas.Fill.Kind  := TBrushKind.bkSolid;
  Canvas.Fill.Color := BackgroundColor;
  Canvas.FillRect( ClipRect, 0, 0, AllCorners, 1, TCornerType.ctRound );
  Canvas.Font.Size   := Font.FontSize;
  Canvas.Font.Style  := [TFontStyle.fsBold];
  Canvas.Font.Family := 'Helvetica';
  if ( Text = '' ) and ( csDesigning in ComponentState ) then
  begin
    Caption           := name;
    Canvas.Fill.Color := TAlphaColors.Gray;
  end
  else
  begin
    Caption           := Text;
    Canvas.Fill.Color := TAlphaColors.Black;
  end;
  Canvas.FillText( ClipRect, Caption, True, 1, [], TTextAlign.taLeading, TTextAlign.taLeading );
  Canvas.EndScene;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
function TDPFTextView.GetText: string;
begin
  Result := FText;
{$IFDEF IOS}
  if FUITextView <> nil then
  begin
    Result := UTF8ToString( FUITextView.Text.UTF8String );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFTextView.SetText( const Value: string );
begin
  FText := Value;
{$IFDEF IOS}
  if FUITextView <> nil then
  begin
    FUITextView.SetText( NSStr( FText ) );
  end;
{$ELSE}
  ControlCaption := FText;
  InvalidateRect( RectF( 0, 0, Width, Height ) );;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFTextView.SetTextColor( const Value: TAlphaColor );
begin
  FTextColor := Value;
{$IFDEF IOS}
  if FUITextView <> nil then
  begin
    FUITextView.SetTextColor( TColorToUIColor( FTextColor ) );
  end;
{$ENDIF}
end;

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

// ------------------------------------------------------------------------------
procedure TDPFTextView.SetEditable( const Value: Boolean );
begin
  FEditable := Value;
{$IFDEF IOS}
  if FUITextView <> nil then
  begin
    FUITextView.SetEditable( Value );
  end;
{$ENDIF}
end;

procedure TDPFTextView.setKeyboardType( const Value: TDPFKeyboardType );
begin
  FKeyboardType := Value;
{$IFDEF IOS}
  if FUITextView <> nil then
  begin
    FUITextView.setKeyboardType( Integer( FKeyboardType ) );
  end;
{$ENDIF}
end;

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

constructor TDPFTextViewDelegate.Create( ADPFTextView: TDPFTextView );
begin
  inherited Create;
  FDPFTextView := ADPFTextView;
end;

// ------------------------------------------------------------------------------
{ function TDPFTextViewDelegate.GetObjectiveCClass: PTypeInfo;
  begin
  Result := TypeInfo( DPFITextViewDelegate );
  end; }

// ------------------------------------------------------------------------------
function TDPFTextViewDelegate.textView( textView: UITextView; shouldChangeTextInRange: NSRange; replacementText: NSString ): Boolean;
var
  newLength: Integer;
begin
  result := FDPFTextView.Editable;
  if not result then
    exit;

  newLength := textView.text.length + replacementText.length - shouldChangeTextInRange.length;
  result    := ( FDPFTextView.Maxlength = 0 ) or ( newLength < FDPFTextView.Maxlength + 1 );

end;

// ------------------------------------------------------------------------------
(* procedure TDPFTextViewDelegate.textViewDidBeginEditing( textView: UITextView );
  begin

  end; *)

// ------------------------------------------------------------------------------
procedure TDPFTextViewDelegate.textViewDidChange( textView: UITextView );
begin
  if Assigned( FDPFTextView.OnTextChanged ) then
    FDPFTextView.OnTextChanged( FDPFTextView );
end;

// ------------------------------------------------------------------------------
procedure TDPFTextViewDelegate.scrollViewDidScroll( scrollView: UIScrollView );
begin
end;

// ------------------------------------------------------------------------------
procedure TDPFTextViewDelegate.scrollViewWillBeginDragging( scrollView: UIScrollView ); cdecl;
begin
end;

// ------------------------------------------------------------------------------
procedure TDPFTextViewDelegate.scrollViewDidEndDecelerating( scrollView: UIScrollView ); cdecl;
begin
end;

// ------------------------------------------------------------------------------
(* procedure TDPFTextViewDelegate.textViewDidChangeSelection( textView: UITextView );
  begin

  end; *)

// ------------------------------------------------------------------------------
(* procedure TDPFTextViewDelegate.textViewDidEndEditing( textView: UITextView );
  begin

  end; *)

// ------------------------------------------------------------------------------
(* function TDPFTextViewDelegate.textViewShouldBeginEditing( textView: UITextView ): Boolean;
  begin
  result := FDPFTextView.Editable;
  end; *)

// ------------------------------------------------------------------------------
(* function TDPFTextViewDelegate.textViewShouldEndEditing( textView: UITextView ): Boolean;
  begin
  result := FDPFTextView.Editable;
  end; *)

// ------------------------------------------------------------------------------

{$ENDIF}

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