lazarus/lcl/interfaces/cocoa/cocoacaret.pas
dmitry 6da9bb3520 cocoa: fix the compiler warning
git-svn-id: trunk@59086 -
2018-09-20 02:55:46 +00:00

390 lines
8.7 KiB
ObjectPascal

{
/***************************************************************************
CocoaCaret.pas - Cocoa Caret Emulation
------------------------------------------
copyright (c) Andreas Hausladen
adopted for Lazarus and Cocoa by Lazarus Team
***************************************************************************/
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
unit CocoaCaret;
{$mode objfpc}{$H+}
{$modeswitch objectivec1}
interface
uses
// Bindings
CocoaAll,
// Free Pascal
Classes, SysUtils, Types,
// Widgetset
CocoaGDIObjects, CocoaPrivate;
type
{ TEmulatedCaret }
TEmulatedCaret = class(TObject)
private
FTimerTarget: NSObject;
FTimer: NSTimer;
FOldRect: TRect;
FView: NSView;
FBitmap: TCocoaBitmap;
FWidth, FHeight: Integer;
FPos: TPoint;
FVisible: Boolean;
FVisibleState: Boolean;
FWidgetSetReleased: Boolean;
FHideCount: Integer;
procedure SetPos(const Value: TPoint);
procedure ResetTimer;
protected
procedure DoTimer(Sender: TObject);
procedure DrawCaret; virtual;
procedure SetView(AView: NSView);
procedure InvalidateView;
public
constructor Create;
destructor Destroy; override;
function CreateCaret(AView: NSView; Bitmap: PtrUInt; Width, Height: Integer): Boolean;
function DestroyCaret: Boolean;
function IsValid: Boolean;
function Show: Boolean;
function Hide: Boolean;
property Pos: TPoint read FPos write SetPos;
end;
function CreateCaret(View: NSView; Bitmap: PtrUInt; Width, Height: Integer): Boolean; overload;
function HideCaret(View: NSView): Boolean;
function ShowCaret(View: NSView): Boolean;
function SetCaretPos(X, Y: Integer): Boolean;
function GetCaretPos(var P: TPoint): Boolean;
function DestroyCaret(View: NSView): Boolean;
procedure DrawCaret;
procedure DestroyGlobalCaret;
implementation
type
{ TCaretTimerTarget }
TCaretTimerTarget = objcclass(NSObject)
fCaret: TEmulatedCaret;
procedure CaretEvent(sender: id); message 'CaretEvent:';
end;
var
GlobalCaret: TEmulatedCaret = nil;
procedure GlobalCaretNeeded;
begin
if GlobalCaret = nil then GlobalCaret := TEmulatedCaret.Create;
end;
procedure DrawCaret;
begin
GlobalCaretNeeded;
if Assigned(GlobalCaret) then
GlobalCaret.DrawCaret;
end;
procedure DestroyGlobalCaret;
begin
FreeAndNil(GlobalCaret);
end;
function CreateCaret(View: NSView; Bitmap: PtrUInt; Width, Height: Integer): Boolean;
begin
GlobalCaretNeeded;
if Assigned(GlobalCaret) then
Result := GlobalCaret.CreateCaret(View, Bitmap, Width, Height)
else
Result := false;
end;
function GetCaretBlinkTime: Cardinal;
begin
// TODO: use MacOSAll.GetCaretTime
Result := 600; // our default value
end;
function HideCaret(View: NSView): Boolean;
begin
Result := False;
GlobalCaretNeeded;
if Assigned(GlobalCaret) then
begin
Result := not Assigned(View) or (View = GlobalCaret.FView);
if Result then
Result := GlobalCaret.Hide;
end;
end;
function ShowCaret(View: NSView): Boolean;
begin
//writeln('ShowCaret: ', PtrUInt(View));
Result := False;
GlobalCaretNeeded;
if Assigned(GlobalCaret) then
begin
Result := not Assigned(View) or (view = GlobalCaret.FView);
if Result then
Result := GlobalCaret.Show;
end;
end;
function SetCaretPos(X, Y: Integer): Boolean;
begin
Result := True;
GlobalCaretNeeded;
if Assigned(GlobalCaret) then
GlobalCaret.Pos := Classes.Point(X, Y);
end;
function GetCaretPos(var P: TPoint): Boolean;
begin
Result := True;
GlobalCaretNeeded;
if Assigned(GlobalCaret) then
begin
with GlobalCaret.Pos do
begin
P.x := X;
P.y := Y;
end;
end;
end;
function DestroyCaret(View: NSView): Boolean;
begin
Result := False;
if Assigned(GlobalCaret) then
begin
Result := not Assigned(View) or (GlobalCaret.FView = View);
if Result then
Result := GlobalCaret.DestroyCaret;
end;
end;
procedure CocoaDisableTimer(var ATimer: NSTimer);
begin
if not Assigned(ATimer) then Exit;
ATimer.invalidate;
ATimer := nil;
end;
function CocoaEnableTimer(trg: id): NSTimer;
begin
Result:=NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats(
GetCaretBlinkTime / 1000, trg, ObjCSelector('CaretEvent:'), nil, true);
end;
{ TCaretTimerTarget }
procedure TCaretTimerTarget.CaretEvent(sender: id);
begin
if not Assigned(fCaret) then Exit;
fCaret.DoTimer(nil);
end;
{ TEmulatedCaret }
constructor TEmulatedCaret.Create;
begin
inherited Create;
FOldRect := Rect(0, 0, 1, 1);
FTimerTarget := TCaretTimerTarget.alloc.init;
TCaretTimerTarget(FTimerTarget).fCaret := Self;
end;
destructor TEmulatedCaret.Destroy;
begin
DestroyCaret;
FTimerTarget.release;
inherited Destroy;
end;
function TEmulatedCaret.CreateCaret(AView: NSView; Bitmap: PtrUInt;
Width, Height: Integer): Boolean;
begin
DestroyCaret;
SetView(AView);
FVisible := false;
FHideCount := 0;
FWidth := Width;
FHeight := Height;
if Bitmap > 1 then
FBitmap := TCocoaBitmap.Create(TCocoaBitmap(Bitmap))
else
FBitmap := nil;
Result := IsValid;
end;
function TEmulatedCaret.DestroyCaret: Boolean;
begin
if Assigned(FView) then
begin
InvalidateView;
if Assigned(FView.lclGetCallback) then
FView.lclGetCallback.SetHasCaret(false);
end;
CocoaDisableTimer(FTimer);
FreeAndNil(FBitmap);
FView := nil;
FWidth := 0;
FHeight := 0;
Result := not IsValid;
end;
procedure TEmulatedCaret.DrawCaret;
begin
//DebugLn('DrawCaret ' + DbgSName(FView.LCLObject) + ' ' + DbgS(FPos) + ' ' + DbgS(FVisible) + ' ' + DbgS(FVisibleState));
//writeln('draw ', FHideCount);
if IsValid and FVisible and FVisibleState and FView.lclIsPainting and (FHideCount = 0) then
begin
if FBitmap = nil then
FView.lclGetCallback.GetContext.InvertRectangle(FPos.X, FPos.Y,
FPos.X + FWidth, FPos.Y + FHeight)
else
FView.lclGetCallback.GetContext.DrawBitmap(FPos.X, FPos.Y,
FBitmap);
end;
end;
function TEmulatedCaret.Show: Boolean;
begin
//writeln('car: ', (AView = FView),' ',(IsValid),' ',Assigned(FView));
Result := (IsValid) and Assigned(FView);
if not Result then Exit;
if (FHideCount > 0) then dec(FHideCount);
//DebugLn('ShowCaret ' + DbgSName(AView.LCLObject));
if not FVisible then
begin
// was not previously visible
InvalidateView;
FVisible := True;
FVisibleState := true;
end;
if not Assigned(FTimer) then ResetTimer;
end;
function TEmulatedCaret.Hide: Boolean;
begin
Result := IsValid;
// inside of paint, there's no need to stop timer and invalidate the drawing
inc(FHideCount);
if Assigned(FView) and (FView.lclIsPainting) then Exit;
if FVisible then
begin
CocoaDisableTimer(FTimer);
FVisible := False;
InvalidateView;
end;
end;
procedure TEmulatedCaret.SetPos(const Value: TPoint);
begin
//DebugLn('SetCaretPos ' + DbgSName(FView.LCLObject));
if FView = nil then
begin
FPos.X := 0;
FPos.Y := 0;
Exit;
end;
if ((FPos.x <> Value.x) or (FPos.y <> Value.y)) then
begin
FPos := Value;
// the caret must remain visible while changing position
FVisibleState := True;
ResetTimer;
if not FView.lclIsPainting then InvalidateView;
end;
end;
procedure TEmulatedCaret.DoTimer(Sender: TObject);
begin
FVisibleState := not FVisibleState;
if FVisible then InvalidateView;
end;
function TEmulatedCaret.IsValid: Boolean;
begin
Result := (FWidth > 0) and (FHeight > 0) and (FView <> nil) and FView.lclIsVisible
and Assigned(FView.lclGetTarget);
end;
procedure TEmulatedCaret.SetView(AView: NSView);
begin
FView := AView;
if FView <> nil then FView.lclGetCallback.HasCaret := True;
CocoaDisableTimer(FTimer);
if Assigned(FView) then
FTimer:=CocoaEnableTimer(FTimerTarget);
end;
procedure TEmulatedCaret.InvalidateView;
var
R: TRect;
begin
if (FView = nil) or FWidgetSetReleased then Exit;
if FView.lclIsPainting then Exit;
if not IsValid then Exit;
//DebugLn('UpdateCaret ' + DbgSName(FView.LCLObject) + ' ' + DbgS(FPos) + ' ' + DbgS(FVisible) + ' ' + DbgS(FVisibleState));
R.Left := FPos.x;
R.Top := FPos.y;
R.Right := R.Left + FWidth + 2;
R.Bottom := R.Top + FHeight + 2;
if not EqualRect(FOldRect, R) then FView.lclInvalidateRect(FOldRect);
FView.lclInvalidateRect(R);
FOldRect := R;
end;
procedure TEmulatedCaret.ResetTimer;
begin
CocoaDisableTimer(FTimer);
FTimer:=CocoaEnableTimer(FTimerTarget);
end;
finalization
DestroyGlobalCaret;
end.