mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-04 04:18:25 +02:00
395 lines
11 KiB
ObjectPascal
395 lines
11 KiB
ObjectPascal
unit CocoaWSCommon;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$modeswitch objectivec1}
|
|
|
|
interface
|
|
|
|
uses
|
|
Types,
|
|
MacOSAll, CocoaAll,
|
|
Classes, Controls, SysUtils,
|
|
//
|
|
WSControls, LCLType, LMessages, LCLProc, Forms,
|
|
CocoaPrivate, CocoaGDIObjects, CocoaUtils, LCLMessageGlue;
|
|
|
|
type
|
|
|
|
{ LCLWSViewExtension }
|
|
|
|
LCLWSViewExtension = objccategory(NSView)
|
|
function lclInitWithCreateParams(const AParams: TCreateParams): id; message 'lclInitWithCreateParams:';
|
|
end;
|
|
|
|
{ TLCLCommonCallback }
|
|
|
|
TLCLCommonCallback = class(TObject, ICommonCallBack)
|
|
public
|
|
Owner: NSObject;
|
|
Target : TWinControl;
|
|
Context : TCocoaContext;
|
|
constructor Create(AOwner: NSObject; ATarget: TWinControl); virtual;
|
|
destructor Destroy; override;
|
|
procedure MouseDown(x,y: Integer); virtual;
|
|
procedure MouseUp(x,y: Integer); virtual;
|
|
procedure MouseClick(clickCount: Integer); virtual;
|
|
procedure MouseMove(x,y: Integer); virtual;
|
|
procedure frameDidChange; virtual;
|
|
procedure boundsDidChange; virtual;
|
|
procedure Draw(ControlContext: NSGraphicsContext; const bounds, dirty: NSRect); virtual;
|
|
function ResetCursorRects: Boolean; virtual;
|
|
end;
|
|
|
|
TLCLCommonCallBackClass = class of TLCLCommonCallBack;
|
|
|
|
{ TCocoaWSWinControl }
|
|
|
|
TCocoaWSWinControl=class(TWSWinControl)
|
|
published
|
|
class function CreateHandle(const AWinControl: TWinControl;
|
|
const AParams: TCreateParams): TLCLIntfHandle; override;
|
|
class procedure SetText(const AWinControl: TWinControl; const AText: String); override;
|
|
class function GetText(const AWinControl: TWinControl; var AText: String): Boolean; override;
|
|
class function GetTextLen(const AWinControl: TWinControl; var ALength: Integer): Boolean; override;
|
|
|
|
class function GetClientBounds(const AWincontrol: TWinControl; var ARect: TRect): Boolean; override;
|
|
class function GetClientRect(const AWincontrol: TWinControl; var ARect: TRect): Boolean; override;
|
|
class procedure SetBounds(const AWinControl: TWinControl; const ALeft, ATop, AWidth, AHeight: Integer); override;
|
|
class procedure SetCursor(const AWinControl: TWinControl; const ACursor: HCursor); override;
|
|
end;
|
|
|
|
|
|
{ TCocoaWSCustomControl }
|
|
|
|
TCocoaWSCustomControl=class(TWSCustomControl)
|
|
published
|
|
class function CreateHandle(const AWinControl: TWinControl;
|
|
const AParams: TCreateParams): TLCLIntfHandle; override;
|
|
end;
|
|
|
|
|
|
// Utility WS functions
|
|
|
|
function AllocCustomControl(const AWinControl: TWinControl): TCocoaCustomControl;
|
|
function EmbedInScrollView(AView: NSView): TCocoaScrollView;
|
|
procedure SetViewDefaults(AView: NSView);
|
|
|
|
implementation
|
|
|
|
function AllocCustomControl(const AWinControl: TWinControl): TCocoaCustomControl;
|
|
begin
|
|
if not Assigned(AWinControl) then begin
|
|
Result:=nil;
|
|
Exit;
|
|
end;
|
|
Result:=TCocoaCustomControl(TCocoaCustomControl.alloc).init;
|
|
Result.callback:=TLCLCommonCallback.Create(Result, AWinControl);
|
|
end;
|
|
|
|
function EmbedInScrollView(AView:NSView):TCocoaScrollView;
|
|
var
|
|
r : TRect;
|
|
p : NSView;
|
|
begin
|
|
if not Assigned(AView) then begin
|
|
Result:=nil;
|
|
Exit;
|
|
end;
|
|
r:=AView.lclFrame;
|
|
p:=AView.superview;
|
|
Result:=TCocoaScrollView.alloc.initWithFrame(NSNullRect);
|
|
if Assigned(p) then p.addSubView(Result);
|
|
Result.lclSetFrame(r);
|
|
Result.setDocumentView(AView);
|
|
SetViewDefaults(Result);
|
|
end;
|
|
|
|
procedure SetViewDefaults(AView:NSView);
|
|
begin
|
|
if not Assigned(AView) then Exit;
|
|
AView.setAutoresizingMask(NSViewMinYMargin or NSViewMaxXMargin);
|
|
end;
|
|
|
|
|
|
{ TLCLCommonCallback }
|
|
|
|
constructor TLCLCommonCallback.Create(AOwner: NSObject; ATarget: TWinControl);
|
|
begin
|
|
inherited Create;
|
|
Owner := AOwner;
|
|
Target := ATarget;
|
|
end;
|
|
|
|
destructor TLCLCommonCallback.Destroy;
|
|
begin
|
|
Context.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.MouseDown(x, y: Integer);
|
|
begin
|
|
LCLSendMouseDownMsg(Target,x,y,mbLeft, []);
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.MouseUp(x, y: Integer);
|
|
begin
|
|
LCLSendMouseUpMsg(Target,x,y,mbLeft, []);
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.MouseClick(clickCount: Integer);
|
|
begin
|
|
LCLSendClickedMsg(Target);
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.MouseMove(x, y: Integer);
|
|
begin
|
|
LCLSendMouseMoveMsg(Target, x,y, []);
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.frameDidChange;
|
|
begin
|
|
boundsDidChange;
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.boundsDidChange;
|
|
var
|
|
NewBounds, OldBounds: TRect;
|
|
PosMsg: TLMWindowPosChanged;
|
|
Resized, Moved, ClientResized: Boolean;
|
|
SizeType: Integer;
|
|
begin
|
|
NewBounds := Owner.lclFrame;
|
|
OldBounds := Target.BoundsRect;
|
|
|
|
Resized :=
|
|
(OldBounds.Right - OldBounds.Left <> NewBounds.Right - NewBounds.Left) or
|
|
(OldBounds.Bottom - OldBounds.Top <> NewBounds.Bottom - NewBounds.Top);
|
|
Moved :=
|
|
(OldBounds.Left <> NewBounds.Left) or
|
|
(OldBounds.Top <> NewBounds.Top);
|
|
|
|
ClientResized := False;
|
|
|
|
// send window pos changed
|
|
if Resized or Moved then
|
|
begin
|
|
PosMsg.Msg := LM_WINDOWPOSCHANGED;
|
|
PosMsg.Result := 0;
|
|
New(PosMsg.WindowPos);
|
|
try
|
|
with PosMsg.WindowPos^ do
|
|
begin
|
|
hWndInsertAfter := 0;
|
|
x := NewBounds.Left;
|
|
y := NewBounds.Right;
|
|
cx := NewBounds.Right - NewBounds.Left;
|
|
cy := NewBounds.Bottom - NewBounds.Top;
|
|
flags := 0;
|
|
end;
|
|
DeliverMessage(Target, PosMsg);
|
|
finally
|
|
Dispose(PosMsg.WindowPos);
|
|
end;
|
|
end;
|
|
|
|
// update client rect
|
|
if Resized or Target.ClientRectNeedsInterfaceUpdate then
|
|
begin
|
|
Target.InvalidateClientRectCache(False);
|
|
ClientResized := True;
|
|
end;
|
|
|
|
// then send a LM_SIZE message
|
|
if Resized or ClientResized then
|
|
begin
|
|
LCLSendSizeMsg(Target, NewBounds.Right - NewBounds.Left,
|
|
NewBounds.Bottom - NewBounds.Top, Owner.lclWindowState, True);
|
|
end;
|
|
|
|
// then send a LM_MOVE message
|
|
if Moved then
|
|
begin
|
|
LCLSendMoveMsg(Target, NewBounds.Left,
|
|
NewBounds.Top, Move_SourceIsInterface);
|
|
end;
|
|
end;
|
|
|
|
procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext;
|
|
const bounds, dirty:NSRect);
|
|
var
|
|
struct : TPaintStruct;
|
|
begin
|
|
if not Assigned(Context) then Context:=TCocoaContext.Create;
|
|
|
|
Context.ctx:=ControlContext;
|
|
if Context.InitDraw(Round(bounds.size.width), Round(bounds.size.height)) then
|
|
begin
|
|
FillChar(struct, SizeOf(TPaintStruct), 0);
|
|
struct.hdc := HDC(Context);
|
|
{$IFDEF VerboseWinAPI}
|
|
DebugLn(Format('[TLCLCommonCallback.Draw] OnPaint event started context: %x', [HDC(context)]));
|
|
{$ENDIF}
|
|
LCLSendPaintMsg(Target, HDC(Context), @struct);
|
|
{$IFDEF VerboseWinAPI}
|
|
DebugLn('[TLCLCommonCallback.Draw] OnPaint event ended');
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
function TLCLCommonCallback.ResetCursorRects: Boolean;
|
|
var
|
|
ACursor: TCursor;
|
|
AControl: TControl;
|
|
View: NSView;
|
|
begin
|
|
Result := False;
|
|
if Owner.isKindOfClass_(NSWindow) then
|
|
View := NSwindow(Owner).contentView
|
|
else
|
|
if Owner.isKindOfClass_(NSView) then
|
|
View := NSView(Owner)
|
|
else
|
|
Exit;
|
|
if not (csDesigning in Target.ComponentState) then
|
|
begin
|
|
ACursor := Screen.Cursor;
|
|
if ACursor = crDefault then
|
|
begin
|
|
// traverse visible child controls
|
|
ACursor := Target.Cursor;
|
|
end;
|
|
Result := ACursor <> crDefault;
|
|
if Result then
|
|
View.addCursorRect_cursor(View.visibleRect, TCocoaCursor(Screen.Cursors[ACursor]).Cursor);
|
|
end;
|
|
end;
|
|
|
|
{ TCocoaWSWinControl }
|
|
|
|
class function TCocoaWSWinControl.CreateHandle(const AWinControl: TWinControl;
|
|
const AParams: TCreateParams): TLCLIntfHandle;
|
|
begin
|
|
Result:=TCocoaWSCustomControl.CreateHandle(AWinControl, AParams);
|
|
end;
|
|
|
|
class procedure TCocoaWSWinControl.SetText(const AWinControl: TWinControl; const AText: String);
|
|
var
|
|
obj : NSObject;
|
|
begin
|
|
// sanity check
|
|
obj:=NSObject(AWinControl.Handle);
|
|
if not Assigned(obj) or not obj.isKindOfClass_(NSControl) then Exit;
|
|
|
|
SetNSControlValue(NSControl(obj), AText);
|
|
end;
|
|
|
|
class function TCocoaWSWinControl.GetText(const AWinControl: TWinControl; var AText: String): Boolean;
|
|
var
|
|
obj : NSObject;
|
|
begin
|
|
Result:=false;
|
|
|
|
// sanity check
|
|
obj:=NSObject(AWinControl.Handle);
|
|
Result:=Assigned(obj) and obj.isKindOfClass_(NSControl);
|
|
if not Result then Exit;
|
|
|
|
AText:=GetNSControlValue(NSControl(obj));
|
|
Result:=true;
|
|
end;
|
|
|
|
class function TCocoaWSWinControl.GetTextLen(const AWinControl: TWinControl; var ALength: Integer): Boolean;
|
|
var
|
|
obj : NSObject;
|
|
s : NSString;
|
|
begin
|
|
Result:=false;
|
|
|
|
// sanity check
|
|
obj:=NSObject(AWinControl.Handle);
|
|
Result:= Assigned(obj) and obj.isKindOfClass_(NSControl);
|
|
if not Result then Exit;
|
|
|
|
s:=NSControl(obj).stringValue;
|
|
if Assigned(s) then ALength:=s.length
|
|
else ALength:=0
|
|
end;
|
|
|
|
class function TCocoaWSWinControl.GetClientBounds(const AWincontrol: TWinControl; var ARect: TRect): Boolean;
|
|
begin
|
|
Result:=(AWinControl.Handle<>0);
|
|
if not Result then Exit;
|
|
ARect:=NSObject(AWinControl.Handle).lclClientFrame;
|
|
end;
|
|
|
|
class function TCocoaWSWinControl.GetClientRect(const AWincontrol: TWinControl; var ARect: TRect): Boolean;
|
|
begin
|
|
Result:=(AWinControl.Handle<>0);
|
|
if not Result then Exit;
|
|
ARect:=NSObject(AWinControl.Handle).lclClientFrame;
|
|
end;
|
|
|
|
class procedure TCocoaWSWinControl.SetBounds(const AWinControl: TWinControl;
|
|
const ALeft, ATop, AWidth, AHeight: Integer);
|
|
begin
|
|
if (AWinControl.Handle<>0) then
|
|
NSObject(AWinControl.Handle).lclSetFrame(Bounds(ALeft, ATop, AWidth, AHeight));
|
|
end;
|
|
|
|
class procedure TCocoaWSWinControl.SetCursor(const AWinControl: TWinControl;
|
|
const ACursor: HCursor);
|
|
var
|
|
Obj: NSObject;
|
|
begin
|
|
if (AWinControl.Handle <> 0) then
|
|
begin
|
|
Obj := NSObject(AWinControl.Handle);
|
|
if Obj.isKindOfClass_(NSWindow) then
|
|
NSWindow(Obj).resetCursorRects
|
|
else
|
|
if Obj.isKindOfClass_(NSView) then
|
|
NSView(Obj).resetCursorRects;
|
|
end;
|
|
end;
|
|
|
|
{ TCocoaWSCustomControl }
|
|
|
|
class function TCocoaWSCustomControl.CreateHandle(const AWinControl: TWinControl;
|
|
const AParams: TCreateParams): TLCLIntfHandle;
|
|
var
|
|
ctrl : TCocoaCustomControl;
|
|
begin
|
|
ctrl:=TCocoaCustomControl( NSView(TCocoaCustomControl.alloc).lclInitWithCreateParams(AParams));
|
|
ctrl.callback:=TLCLCommonCallback.Create(ctrl, AWinControl);
|
|
Result:=TLCLIntfHandle(ctrl);
|
|
end;
|
|
|
|
{ LCLWSViewExtension }
|
|
|
|
function LCLWSViewExtension.lclInitWithCreateParams(const AParams:TCreateParams): id;
|
|
var
|
|
p: NSView;
|
|
ns: NSRect;
|
|
begin
|
|
p:=nil;
|
|
if (AParams.WndParent<>0) then begin
|
|
if (NSObject(AParams.WndParent).isKindOfClass_(NSView)) then
|
|
p:=NSView(AParams.WndParent)
|
|
else if (NSObject(AParams.WndParent).isKindOfClass_(NSWindow)) then
|
|
p:=NSWindow(AParams.WndParent).contentView;
|
|
end;
|
|
with AParams do
|
|
if Assigned(p)
|
|
then LCLToNSRect(Types.Bounds(X,Y,Width, Height), p.frame.size.height, ns)
|
|
else LCLToNSRect(Types.Bounds(X,Y,Width, Height), ns);
|
|
|
|
Result:=initWithFrame(ns);
|
|
if not Assigned(Result) then Exit;
|
|
|
|
if Assigned(p) then p.addSubview(Self);
|
|
SetViewDefaults(Self);
|
|
end;
|
|
|
|
end.
|
|
|