mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-25 04:19:23 +02:00
1042 lines
32 KiB
ObjectPascal
1042 lines
32 KiB
ObjectPascal
{ -----------------------------------------
|
|
carbondef.pp - Type & Const definitions
|
|
-----------------------------------------
|
|
|
|
@created(Wed Aug 26st WET 2005)
|
|
@lastmod($Date$)
|
|
@author(Marc Weustink <marc@@lazarus.dommelstein.net>)
|
|
|
|
This unit contains type & const definitions needed in the Carbon <-> LCL interface
|
|
|
|
*****************************************************************************
|
|
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 CarbonDef;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
// defines
|
|
{$I carbondefines.inc}
|
|
|
|
uses
|
|
// libs
|
|
MacOSAll,
|
|
// wdgetset
|
|
WSLCLClasses, Classes, SysUtils, Controls, LCLType, LCLProc, Graphics, Contnrs,
|
|
Laz_AVL_Tree, LMessages, LCLMessageGlue;
|
|
|
|
var
|
|
LAZARUS_FOURCC: FourCharCode; // = 'Laz ';
|
|
WIDGETINFO_FOURCC: FourCharCode; // = 'WInf';
|
|
MENU_FOURCC: FourCharCode; // = 'Menu';
|
|
CREATESHEETWINDOW: PtrUInt = 0; // used to create sheet inside modal form.issue #21459
|
|
|
|
type
|
|
|
|
{ TCarbonContext }
|
|
|
|
TCarbonContext = class
|
|
public
|
|
CGContext : CGContextRef;
|
|
procedure Reset; virtual; abstract;
|
|
function GetLogicalOffset: TPoint; virtual;
|
|
end;
|
|
|
|
{ TCarbonWidget }
|
|
|
|
TCarbonWidget = class
|
|
private
|
|
FScrollOffset: TPoint;
|
|
FEventProcCount: Integer;
|
|
FProperties: TStringList;
|
|
FCursor: HCURSOR;
|
|
FHasCaret: Boolean;
|
|
FHasPaint: Boolean;
|
|
FResizing: Boolean;
|
|
FBoundsReported: Boolean;
|
|
function GetPainting: Boolean;
|
|
function GetProperty(AIndex: String): Pointer;
|
|
function GetScrollOffset: TPoint;
|
|
procedure SetProperty(AIndex: String; const AValue: Pointer);
|
|
procedure SetScrollOffset(AValue: TPoint);
|
|
protected
|
|
procedure RegisterEvents; virtual; abstract;
|
|
procedure CreateWidget(const AParams: TCreateParams); virtual; abstract;
|
|
procedure DestroyWidget; virtual; abstract;
|
|
function GetContent: ControlRef; virtual; abstract;
|
|
procedure UpdateLCLClientRect; virtual;
|
|
public
|
|
FPopupWin: WindowRef;
|
|
FNeedFree: Boolean;
|
|
procedure BeginEventProc;
|
|
procedure EndEventProc;
|
|
function isEventProcessing: Boolean;
|
|
procedure FreeCarbonWidget;
|
|
public
|
|
LCLObject: TWinControl; // LCL control which created this widget
|
|
Context: TCarbonContext; // Carbon content area context
|
|
Widget: HIViewRef; // Reference to the Carbon control
|
|
public
|
|
procedure FocusSet; virtual;
|
|
procedure FocusKilled; virtual;
|
|
procedure BoundsChanged; virtual;
|
|
procedure ControlAdded; virtual;
|
|
function FilterKeyPress({%H-}SysKey: Boolean; const {%H-}Char: TUTF8Char): Boolean; virtual;
|
|
procedure ProcessKeyEvent(const {%H-}msg: TLMKey); virtual;
|
|
function NeedDeliverMouseEvent({%H-}Msg: Integer; const {%H-}AMessage): Boolean; virtual;
|
|
public
|
|
constructor Create(const AObject: TWinControl; const AParams: TCreateParams);
|
|
destructor Destroy; override;
|
|
procedure AddToWidget(AParent: TCarbonWidget); virtual; abstract;
|
|
function GetClientRect(var ARect: TRect): Boolean; virtual; abstract;
|
|
function GetPreferredSize: TPoint; virtual;
|
|
function GetWindowRelativePos(winX, winY: Integer): TPoint; virtual; abstract;
|
|
function GetMousePos: TPoint;
|
|
function GetTopParentWindow: WindowRef; virtual; abstract;
|
|
procedure Invalidate(Rect: PRect = nil); virtual; abstract;
|
|
procedure InvalidateRgn(AShape: HISHapeRef);
|
|
function IsDesignInteractive(const {%H-}P: TPoint): Boolean; virtual;
|
|
function IsEnabled: Boolean; virtual; abstract;
|
|
function IsVisible: Boolean; virtual; abstract;
|
|
function Enable(AEnable: Boolean): Boolean; virtual; abstract;
|
|
|
|
function GetNextFocus(Start: TCarbonWidget; Next: Boolean): ControlRef;
|
|
procedure GetScrollInfo({%H-}SBStyle: Integer; var {%H-}ScrollInfo: TScrollInfo); virtual;
|
|
function GetScrollbarVisible({%H-}SBStyle: Integer): Boolean; virtual;
|
|
function GetBounds(var ARect: TRect): Boolean; virtual; abstract;
|
|
function GetScreenBounds(var ARect: TRect): Boolean; virtual; abstract;
|
|
function SetBounds(const ARect: TRect): Boolean; virtual; abstract;
|
|
procedure SetChildZPosition(AChild: TCarbonWidget; const AOldPos, ANewPos: Integer; const AChildren: TFPList); virtual;
|
|
procedure SetZOrder(AOrder: HIViewZOrderOp; ARefWidget: TCarbonWidget); virtual; abstract;
|
|
procedure SetCursor(ACursor: HCURSOR); virtual;
|
|
|
|
procedure ScrollBy(DX, DY: Integer); virtual;
|
|
procedure ScrollRect(DX, DY: Integer; ARect: TRect); virtual;
|
|
procedure SetFocus; virtual; abstract;
|
|
procedure SetColor(const AColor: TColor); virtual; abstract;
|
|
function SetScrollInfo({%H-}SBStyle: Integer; const {%H-}ScrollInfo: TScrollInfo): Integer; virtual;
|
|
procedure SetFont(const AFont: TFont); virtual; abstract;
|
|
procedure ShowHide(AVisible: Boolean); virtual; abstract;
|
|
|
|
function GetText(var S: String): Boolean; virtual; abstract;
|
|
function SetText(const S: String): Boolean; virtual; abstract;
|
|
function Update: Boolean; virtual; abstract;
|
|
|
|
function WidgetAtPos(const P: TPoint): ControlRef; virtual; abstract;
|
|
public
|
|
property BoundsReported: Boolean read FBoundsReported;
|
|
{ Content:
|
|
= widget in controls without special client control
|
|
- client area control of control or window
|
|
- origin of local coordinates
|
|
- area for embedding child controls
|
|
- processes track and draw event }
|
|
property Content: ControlRef read GetContent;
|
|
property Cursor: HCURSOR read FCursor;
|
|
property ScrollOffset: TPoint read GetScrollOffset write SetScrollOffset; // scrolled offset of ScrollingWinControl
|
|
property HasCaret: Boolean read FHasCaret write FHasCaret;
|
|
property HasPaint: Boolean read FHasPaint write FHasPaint;
|
|
property Painting: Boolean read GetPainting;
|
|
property Properties[AIndex: String]: Pointer read GetProperty write SetProperty;
|
|
property Resizing: Boolean read FResizing write FResizing;
|
|
end;
|
|
|
|
type
|
|
TCarbonObjectEventHandlerProc = function (ANextHandler: EventHandlerCallRef;
|
|
AEvent: EventRef;
|
|
AWidget: TObject): OSStatus; {$IFDEF darwin}mwpascal;{$ENDIF}
|
|
|
|
TCarbonEventHandlerProc = function (ANextHandler: EventHandlerCallRef;
|
|
AEvent: EventRef;
|
|
AWidget: TCarbonWidget): OSStatus; {$IFDEF darwin}mwpascal;{$ENDIF}
|
|
|
|
TEventInt = packed record
|
|
case Integer of
|
|
1: (Chars: array[0..4] of Char);
|
|
2: (Int: UInt32);
|
|
end;
|
|
|
|
const
|
|
LCLCarbonEventClass = 'Laz ';
|
|
LCLCarbonEventKindWake = 'Wake';
|
|
LCLCarbonEventKindMain = 'Main';
|
|
LCLCarbonEventKindUser = 'User';
|
|
|
|
function CheckHandle(const AWinControl: TWinControl; const AClass: TClass; const DbgText: String): Boolean;
|
|
function CheckWidget(const Handle: HWND; const AMethodName: String; AParamName: String = ''): Boolean;
|
|
function CheckWidget(const Handle: HWND; const AMethodName: String; AClass: TClass): Boolean;
|
|
|
|
function RegisterObjectEventHandler(AHandler: TCarbonObjectEventHandlerProc): EventHandlerUPP;
|
|
function RegisterEventHandler(AHandler: TCarbonEventHandlerProc): EventHandlerUPP;
|
|
|
|
procedure NeedFreeWidget(AWidget: TCarbonWidget);
|
|
procedure FreePendingWidgets;
|
|
|
|
implementation
|
|
|
|
uses
|
|
CarbonProc, CarbonDbgConsts, CarbonUtils, CarbonCaret;
|
|
|
|
var
|
|
WantFreeList : TFPList;
|
|
|
|
procedure NeedFreeWidget(AWidget: TCarbonWidget);
|
|
begin
|
|
WantFreeList.Add(AWidget);
|
|
end;
|
|
|
|
procedure FreePendingWidgets;
|
|
var
|
|
i : integer;
|
|
begin
|
|
for i:=0 to WantFreeList.Count-1 do
|
|
TCarbonWidget(WantFreeList[i]).Free;
|
|
WantfreeList.Clear;
|
|
end;
|
|
{------------------------------------------------------------------------------
|
|
Name: CheckHandle
|
|
Params: AWinControl - Handle of window
|
|
AClass - Class
|
|
DbgText - Text to output on invalid DC
|
|
Returns: If the wincontrol handle is allocated and valid
|
|
------------------------------------------------------------------------------}
|
|
function CheckHandle(const AWinControl: TWinControl; const AClass: TClass;
|
|
const DbgText: String): Boolean;
|
|
begin
|
|
if AWinControl <> nil then
|
|
begin
|
|
if (AWinControl.HandleAllocated)
|
|
and (TObject(AWinControl.Handle) is TCarbonWidget) then
|
|
begin
|
|
{$IFDEF VerboseWSClass}
|
|
DebugLn(AClass.ClassName + '.' + DbgText + ' for ' + AWinControl.Name);
|
|
{$ENDIF}
|
|
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
Result := False;
|
|
debugln(['CheckHandle failed AWinControl=',DbgSName(AWinControl),' AClass=',DbgSName(AClass),' ',DbgText,' HandleAllocated=',AWinControl.HandleAllocated]);
|
|
DumpStack;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Result := False;
|
|
DebugLn(AClass.ClassName + '.' + DbgText + ' for ' + AWinControl.Name +
|
|
' failed: WinControl is nil!');
|
|
end;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Name: CheckWidget
|
|
Params: Handle - Handle of window
|
|
AMethodName - Method name
|
|
AParamName - Param name
|
|
Returns: If the window is valid widget
|
|
------------------------------------------------------------------------------}
|
|
function CheckWidget(const Handle: HWND; const AMethodName: String;
|
|
AParamName: String): Boolean;
|
|
begin
|
|
if TObject(Handle) is TCarbonWidget then Result := True
|
|
else
|
|
begin
|
|
Result := False;
|
|
|
|
if Pos('.', AMethodName) = 0 then
|
|
DebugLn(SCarbonWSPrefix + AMethodName + ' Error - invalid widget ' +
|
|
AParamName + ' = ' + DbgS(Handle) + '!')
|
|
else
|
|
DebugLn(AMethodName + ' Error - invalid widget ' + AParamName + ' = ' +
|
|
DbgS(Handle) + '!');
|
|
end;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Name: CheckWidget
|
|
Params: Handle - Handle of window
|
|
AMethodName - Method name
|
|
AClass - Class
|
|
Returns: If the window is valid widget and class
|
|
------------------------------------------------------------------------------}
|
|
function CheckWidget(const Handle: HWND; const AMethodName: String;
|
|
AClass: TClass): Boolean;
|
|
var
|
|
S: String;
|
|
begin
|
|
if TObject(Handle) is TCarbonWidget then
|
|
begin
|
|
if TObject(Handle) is AClass then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
|
|
S := ' Error - Widget ' + TObject(Handle).ClassName + ' is not ' +
|
|
AClass.ClassName + '!';
|
|
end
|
|
else S := ' Error - Handle ' + DbgS(Handle) + ' is not valid widget!';
|
|
|
|
Result := False;
|
|
|
|
if Pos('.', AMethodName) = 0 then
|
|
DebugLn(SCarbonWSPrefix + AMethodName + S)
|
|
else
|
|
DebugLn(AMethodName + S);
|
|
end;
|
|
|
|
//=====================================================
|
|
// UPP mamanger
|
|
//=====================================================
|
|
type
|
|
TUPPAVLTreeNode = class(TAVLTreeNode)
|
|
public
|
|
UPP: EventHandlerUPP;
|
|
procedure Clear; reintroduce; // not overridable, so reintroduce since we only will call this clear
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
var
|
|
UPPTree: TAVLTree = nil;
|
|
|
|
{ TCarbonContext }
|
|
|
|
function TCarbonContext.GetLogicalOffset: TPoint;
|
|
begin
|
|
Result.X := 0;
|
|
Result.Y := 0;
|
|
end;
|
|
|
|
procedure TUPPAVLTreeNode.Clear;
|
|
begin
|
|
if UPP <> nil then
|
|
begin
|
|
DisposeEventHandlerUPP(UPP);
|
|
UPP := nil;
|
|
end;
|
|
|
|
inherited Clear;
|
|
end;
|
|
|
|
destructor TUPPAVLTreeNode.Destroy;
|
|
begin
|
|
if UPP <> nil then
|
|
begin
|
|
DisposeEventHandlerUPP(UPP);
|
|
UPP := nil;
|
|
end;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Name: RegisterObjectEventHandler
|
|
Params: AHandler - Carbon object event handler procedure
|
|
Returns: Event handler UPP
|
|
|
|
Registers new carbon object event handler procedure
|
|
------------------------------------------------------------------------------}
|
|
function RegisterObjectEventHandler(AHandler: TCarbonObjectEventHandlerProc): EventHandlerUPP;
|
|
var
|
|
Node: TUPPAVLTreeNode;
|
|
begin
|
|
if UPPTree = nil then
|
|
begin
|
|
UPPTree := TAVLTree.Create;
|
|
UPPTree.NodeClass:=TUPPAVLTreeNode;
|
|
end;
|
|
|
|
Node := TUPPAVLTreeNode(UPPTree.Find(AHandler));
|
|
if Node = nil then
|
|
begin
|
|
Node := TUPPAVLTreeNode.Create;
|
|
Node.Data := AHandler;
|
|
Node.UPP := NewEventHandlerUPP(EventHandlerProcPtr(AHandler));
|
|
UPPTree.Add(Node);
|
|
end;
|
|
|
|
Result := Node.UPP;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Name: RegisterEventHandler
|
|
Params: AHandler - Carbon event handler procedure
|
|
Returns: Event handler UPP
|
|
|
|
Registers new carbon event handler procedure
|
|
------------------------------------------------------------------------------}
|
|
function RegisterEventHandler(AHandler: TCarbonEventHandlerProc): EventHandlerUPP;
|
|
var
|
|
Node: TUPPAVLTreeNode;
|
|
begin
|
|
if UPPTree = nil then
|
|
begin
|
|
UPPTree := TAVLTree.Create;
|
|
UPPTree.NodeClass:=TUPPAVLTreeNode;
|
|
end;
|
|
|
|
Node := TUPPAVLTreeNode(UPPTree.Find(AHandler));
|
|
if Node = nil then
|
|
begin
|
|
Node := TUPPAVLTreeNode.Create;
|
|
Node.Data := AHandler;
|
|
Node.UPP := NewEventHandlerUPP(EventHandlerProcPtr(AHandler));
|
|
UPPTree.Add(Node);
|
|
end;
|
|
|
|
Result := Node.UPP;
|
|
end;
|
|
|
|
{ TCarbonWidget }
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.GetProperty
|
|
Params: AIndex - Property name
|
|
Returns: Property data, nil if the property is not listed
|
|
|
|
Returns the specified property data or nil if the property is not listed
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.GetProperty(AIndex: String): Pointer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FProperties <> nil then
|
|
begin
|
|
I := FProperties.IndexOf(AIndex);
|
|
|
|
if I >= 0 then // the property is listed
|
|
begin
|
|
Result := FProperties.Objects[I];
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
function TCarbonWidget.GetScrollOffset: TPoint;
|
|
begin
|
|
Result := Point(-FScrollOffset.X, -FScrollOffset.Y);
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.GetPainting
|
|
Returns: If the widget is being repaint
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.GetPainting: Boolean;
|
|
begin
|
|
Result := Context <> nil;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.SetProperty
|
|
Params: AIndex - Property name
|
|
AValue - Property data, nil means remove the property
|
|
|
|
Sets the specified property data or removes the property
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.SetProperty(AIndex: String; const AValue: Pointer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FProperties = nil then
|
|
begin
|
|
if AValue = nil then Exit;
|
|
// create string list for storing properties
|
|
FProperties := TStringList.Create;
|
|
FProperties.Sorted := True; // to enable binary searching
|
|
end;
|
|
|
|
I := FProperties.IndexOf(AIndex);
|
|
if I >= 0 then // the property is listed -> update or remove if AValue = nil
|
|
begin
|
|
if AValue = nil then
|
|
begin
|
|
FProperties.Delete(I);
|
|
if FProperties.Count = 0 then
|
|
begin
|
|
FProperties.Free; // free if the list is clear
|
|
FProperties := nil;
|
|
end;
|
|
end
|
|
else FProperties.Objects[I] := TObject(AValue);
|
|
end
|
|
else // the property is not listed -> add
|
|
begin
|
|
FProperties.AddObject(AIndex, TObject(AValue));
|
|
end;
|
|
end;
|
|
|
|
procedure TCarbonWidget.SetScrollOffset(AValue: TPoint);
|
|
begin
|
|
FScrollOffset := AValue;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.UpdateLCLClientRect
|
|
|
|
Updates client rect of LCL object
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.UpdateLCLClientRect;
|
|
var
|
|
R: TRect;
|
|
ClientR: TRect;
|
|
LCLR: TRect;
|
|
LCLClientR: TRect;
|
|
RChanged: Boolean;
|
|
ClientChanged: Boolean;
|
|
begin
|
|
if not Resizing then begin
|
|
GetBounds(R{%H-});
|
|
GetClientRect(ClientR{%H-});
|
|
LCLR:=LCLObject.BoundsRect;
|
|
LCLClientR:=LCLObject.ClientRect;
|
|
RChanged:=not CompareRect(@R,@LCLR);
|
|
ClientChanged:=not CompareRect(@ClientR,@LCLClientR);
|
|
|
|
if not ClientChanged then
|
|
LCLObject.InvalidateClientRectCache(False);
|
|
if RChanged or ClientChanged then
|
|
LCLSendSizeMsg(LCLObject, R.Right - R.Left, R.Bottom - R.Top, Size_SourceIsInterface);
|
|
end;
|
|
end;
|
|
|
|
procedure TCarbonWidget.BeginEventProc;
|
|
begin
|
|
inc(FEventProcCount);
|
|
end;
|
|
|
|
procedure TCarbonWidget.EndEventProc;
|
|
begin
|
|
dec(FEventProcCount);
|
|
if (FEventProcCount=0) and FNeedFree then
|
|
NeedFreeWidget(Self)
|
|
end;
|
|
|
|
function TCarbonWidget.isEventProcessing: Boolean;
|
|
begin
|
|
Result:=FEventProcCount>0;
|
|
end;
|
|
|
|
procedure TCarbonWidget.FreeCarbonWidget;
|
|
begin
|
|
if isEventProcessing then
|
|
FNeedFree:=True
|
|
else
|
|
Free;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.FocusSet
|
|
|
|
Handles set focus
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.FocusSet;
|
|
begin
|
|
{$IFDEF VerboseCommonEvent}
|
|
DebugLn('TCarbonWidget.FocusSet: ', DbgSName(LCLObject));
|
|
{$ENDIF}
|
|
LCLSendSetFocusMsg(LCLObject);
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.FocusKilled
|
|
|
|
Handles kill focus
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.FocusKilled;
|
|
begin
|
|
{$IFDEF VerboseCommonEvent}
|
|
DebugLn('TCarbonWidget.FocusKilled: ', DbgSName(LCLObject));
|
|
{$ENDIF}
|
|
// the TCarbonWidget has already been freed, it cannot send any messages
|
|
if not FNeedFree then
|
|
LCLSendKillFocusMsg(LCLObject);
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.BoundsChanged
|
|
|
|
Handles bounds change
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.BoundsChanged;
|
|
var
|
|
{$IFDEF VerboseBounds}
|
|
WidgetClient,
|
|
{$ENDIF}
|
|
WidgetBounds, OldBounds: TRect;
|
|
Resized, ClientResized, Moved: Boolean;
|
|
PosMsg: TLMWindowPosChanged;
|
|
begin
|
|
if FResizing then Exit;
|
|
{$IFDEF VerboseBounds}
|
|
DebugLn('TCarbonWidget.BoundsChanged ' + LCLObject.Name);
|
|
{$ENDIF}
|
|
|
|
GetBounds(WidgetBounds{%H-});
|
|
OldBounds := LCLObject.BoundsRect;
|
|
|
|
{$IFDEF VerboseBounds}
|
|
GetClientRect(WidgetClient);
|
|
DebugLn('TCarbonWidget.BoundsChanged Interface new bounds: ' + DbgS(WidgetBounds));
|
|
DebugLn('TCarbonWidget.BoundsChanged LCL old bounds: ' + DbgS(OldBounds));
|
|
DebugLn('TCarbonWidget.BoundsChanged Interface new client: ' + DbgS(WidgetClient));
|
|
DebugLn('TCarbonWidget.BoundsChanged LCL old client: ' + DbgS(LCLObject.ClientRect));
|
|
{$ENDIF}
|
|
|
|
Resized :=
|
|
(OldBounds.Right - OldBounds.Left <> WidgetBounds.Right - WidgetBounds.Left) or
|
|
(OldBounds.Bottom - OldBounds.Top <> WidgetBounds.Bottom - WidgetBounds.Top) or
|
|
not FBoundsReported;
|
|
Moved :=
|
|
(OldBounds.Left <> WidgetBounds.Left) or
|
|
(OldBounds.Top <> WidgetBounds.Top) or
|
|
not FBoundsReported;
|
|
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 := WidgetBounds.Left;
|
|
y := WidgetBounds.Right;
|
|
cx := WidgetBounds.Right - WidgetBounds.Left;
|
|
cy := WidgetBounds.Bottom - WidgetBounds.Top;
|
|
flags := 0;
|
|
end;
|
|
DeliverMessage(LCLObject, PosMsg);
|
|
finally
|
|
Dispose(PosMsg.WindowPos);
|
|
end;
|
|
end;
|
|
|
|
// update client rect
|
|
if Resized or LCLObject.ClientRectNeedsInterfaceUpdate then
|
|
begin
|
|
{$IFDEF VerboseBounds}
|
|
DebugLn('TCarbonWidget.BoundsChanged Update client rects cache');
|
|
{$ENDIF}
|
|
LCLObject.InvalidateClientRectCache(False);
|
|
ClientResized := True;
|
|
end;
|
|
|
|
// then send a LM_SIZE message
|
|
if Resized or ClientResized then
|
|
begin
|
|
LCLSendSizeMsg(LCLObject, WidgetBounds.Right - WidgetBounds.Left,
|
|
WidgetBounds.Bottom - WidgetBounds.Top, Size_SourceIsInterface);
|
|
end;
|
|
|
|
// then send a LM_MOVE message
|
|
if Moved then
|
|
begin
|
|
LCLSendMoveMsg(LCLObject, WidgetBounds.Left,
|
|
WidgetBounds.Top, Move_SourceIsInterface);
|
|
end;
|
|
|
|
// invalidate client area
|
|
if ClientResized then Invalidate;
|
|
|
|
// invalidate parent client area, previously covered by control
|
|
if Resized and (LCLObject.Parent <> nil) and LCLObject.Parent.HandleAllocated then
|
|
begin
|
|
TCarbonWidget(LCLObject.Parent.Handle).Invalidate(@OldBounds);
|
|
end;
|
|
|
|
{$IFDEF VerboseBounds}
|
|
DebugLn('TCarbonWidget.BoundsChanged LCL new bounds: ' + DbgS(LCLObject.BoundsRect));
|
|
DebugLn('TCarbonWidget.BoundsChanged LCL new client: ' + DbgS(LCLObject.ClientRect));
|
|
{$ENDIF}
|
|
|
|
FBoundsReported := True;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.ControlAdded
|
|
|
|
Notifies about control added
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.ControlAdded;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.FilterKeyPress
|
|
|
|
Filters key presses from being send to Carbon control
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.FilterKeyPress(SysKey: Boolean; const Char: TUTF8Char): Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.ProcessKeyEvent
|
|
Params: msg - LCL keyboard message
|
|
Result - returned value, must be noErr if key is handled
|
|
Returns: The Carbon widget
|
|
|
|
Widget can perform it's own necessary actions if user has not processed the key.
|
|
It's required to emulate Command driven Carbon controls
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.ProcessKeyEvent(const msg: TLMKey);
|
|
begin
|
|
end;
|
|
|
|
function TCarbonWidget.NeedDeliverMouseEvent(Msg: Integer; const AMessage): Boolean;
|
|
begin
|
|
Result := true;
|
|
end;
|
|
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.Create
|
|
Params: AObject - LCL conrol
|
|
AParams - Creation parameters
|
|
Returns: The Carbon widget
|
|
|
|
Creates basic widget for the specified LCL control
|
|
------------------------------------------------------------------------------}
|
|
constructor TCarbonWidget.Create(const AObject: TWinControl;
|
|
const AParams: TCreateParams);
|
|
begin
|
|
FScrollOffset := Point(0, 0);
|
|
LCLObject := AObject;
|
|
FProperties := nil;
|
|
Widget := nil;
|
|
Context := nil;
|
|
FHasCaret := False;
|
|
FHasPaint := False;
|
|
FResizing := False;
|
|
FBoundsReported := False;
|
|
|
|
CreateWidget(AParams);
|
|
|
|
{$IFDEF VerboseWidget}
|
|
DebugLn('TCarbonWidget.Create ', ClassName, ' ', LCLObject.Name, ': ',
|
|
LCLObject.ClassName);
|
|
{$ENDIF}
|
|
|
|
RegisterEvents;
|
|
|
|
{$IFDEF VerboseBounds}
|
|
DebugLn('TCarbonWidget.Create LCL bounds: ' + DbgS(LCLObject.BoundsRect));
|
|
DebugLn('TCarbonWidget.Create LCL client: ' + DbgS(LCLObject.ClientRect));
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.Destroy
|
|
|
|
Frees the widget
|
|
------------------------------------------------------------------------------}
|
|
destructor TCarbonWidget.Destroy;
|
|
begin
|
|
{$IFDEF VerboseWidget}
|
|
DebugLn('TCarbonWidget.Destroy ', ClassName, ' ', LCLObject.Name, ': ',
|
|
LCLObject.ClassName);
|
|
{$ENDIF}
|
|
|
|
DestroyWidget;
|
|
|
|
FProperties.Free;
|
|
|
|
if HasCaret then DestroyCaret;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.GetPreferredSize
|
|
Returns: The preffered size of widget for autosizing or (0, 0)
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.GetPreferredSize: TPoint;
|
|
begin
|
|
Result.X := 0;
|
|
Result.Y := 0;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.GetPreferredSize
|
|
Returns: The current mouse position relative to the widgetset left-top pos
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.GetMousePos: TPoint;
|
|
var
|
|
P: MacOSAll.Point;
|
|
R: MacOSAll.Rect;
|
|
const
|
|
SName = 'GetMousePos';
|
|
begin
|
|
GetGlobalMouse(P{%H-});
|
|
|
|
OSError(GetWindowBounds(GetTopParentWindow, kWindowStructureRgn, R{%H-}),
|
|
Self, SName, SGetWindowBounds);
|
|
Result:=GetWindowRelativePos(P.h - R.left, P.v - R.top);
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.InvalidateRgn
|
|
Params: AShape - HIShapeRef
|
|
|
|
Invalidates the specified client region or entire area
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.InvalidateRgn(AShape: HISHapeRef);
|
|
begin
|
|
if AShape = nil then Invalidate
|
|
else
|
|
OSError(HIViewSetNeedsDisplayInShape(Content, AShape, True),
|
|
Self, 'InvalidateRgn', 'HIViewSetNeedsDisplayInShape');
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.IsDesignInteractive
|
|
Params: P - Client pos
|
|
Returns: If the pos is design interactive
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.IsDesignInteractive(const P: TPoint): Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.GetNextFocus
|
|
Params: Start - Focus start
|
|
Next - Next or previous?
|
|
Returns: Next control to focus
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.GetNextFocus(Start: TCarbonWidget; Next: Boolean): ControlRef;
|
|
var
|
|
StartControl, ParentControl, ResultControl: TWinControl;
|
|
TabList: TFPObjectList;
|
|
TabIndex: Integer;
|
|
|
|
begin
|
|
Result := nil;
|
|
ResultControl := nil;
|
|
|
|
if Start <> nil then
|
|
StartControl := Start.LCLObject
|
|
else
|
|
StartControl := nil;
|
|
|
|
//DebugLn('TCarbonWidget.GetNextFocus ', LCLObject.Name, ' Start: ', DbgSName(StartControl), ' Next: ', DbgS(Next));
|
|
|
|
ParentControl := LCLObject;
|
|
TabList := TFPObjectList.Create(False);
|
|
try
|
|
while (ParentControl <> nil) and (ResultControl = nil) do
|
|
begin
|
|
TabList.Clear;
|
|
ParentControl.GetTabOrderList(TabList.List);
|
|
|
|
TabIndex := -1;
|
|
if StartControl <> nil then
|
|
TabIndex := TabList.IndexOf(StartControl);
|
|
|
|
if (TabList.Count = 0) or
|
|
(Next and (TabIndex > TabList.Count - 2)) or
|
|
(not Next and (TabIndex < 1)) then
|
|
begin
|
|
StartControl := ParentControl;
|
|
|
|
if ParentControl.Parent = nil then
|
|
if Next then
|
|
ResultControl := TabList.First as TWinControl
|
|
else
|
|
ResultControl := TabList.Last as TWinControl;
|
|
end
|
|
else
|
|
if TabIndex = -1 then
|
|
begin
|
|
if Next then
|
|
ResultControl := TabList.First as TWinControl
|
|
else
|
|
ResultControl := TabList.Last as TWinControl;
|
|
end
|
|
else
|
|
if Next then
|
|
ResultControl := TabList[TabIndex + 1] as TWinControl
|
|
else
|
|
ResultControl := TabList[TabIndex - 1] as TWinControl;
|
|
|
|
ParentControl := ParentControl.Parent;
|
|
end;
|
|
finally
|
|
TabList.Free;
|
|
end;
|
|
|
|
if ResultControl <> nil then
|
|
Result := TCarbonWidget(ResultControl.Handle).Widget;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.GetScrollInfo
|
|
Params: SBStyle - Scrollbar type (SB_VERT, SB_HORZ)
|
|
ScrollInfo - Record fo scrolling info
|
|
Returns: If the function suceeds
|
|
|
|
Gets the scrolling info of the specified scroll bar
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.GetScrollInfo(SBStyle: Integer;
|
|
var ScrollInfo: TScrollInfo);
|
|
begin
|
|
// ToDo: TCarbonWidget.GetScrollInfo
|
|
DebugLn(ClassName + '.GetScrollInfo unsupported or not implemented!');
|
|
end;
|
|
|
|
function TCarbonWidget.GetScrollbarVisible(SBStyle: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
// ToDo TCarbonWidget.GetScrollbarVisible
|
|
DebugLn(ClassName + '.GetScrollbarVisible unsupported or not implemented!');
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonControl.SetChildZPosition
|
|
Params: AChild - Child widget
|
|
AOldPos - Old z position
|
|
ANewPos - New z position
|
|
AChildren - List of all child controls
|
|
|
|
Sets the child z position of Carbon widget
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.SetChildZPosition(AChild: TCarbonWidget; const AOldPos, ANewPos: Integer;
|
|
const AChildren: TFPList);
|
|
var
|
|
RefWidget: TCarbonWidget;
|
|
Order: HIViewZOrderOp;
|
|
I, StopPos: Integer;
|
|
Child: TWinControl;
|
|
begin
|
|
RefWidget := nil;
|
|
|
|
if ANewPos <= 0 then // send behind all
|
|
Order := kHIViewZOrderBelow
|
|
else
|
|
if ANewPos >= Pred(AChildren.Count) then // bring to front of all
|
|
Order := kHIViewZOrderAbove
|
|
else // custom position
|
|
begin
|
|
// Search for the first child above us with a handle.
|
|
// The child list is reversed form the windows order.
|
|
// If we don't find an allocated handle then exit.
|
|
|
|
if AOldPos > ANewPos then
|
|
StopPos := AOldPos // the child is moved to the bottom
|
|
else
|
|
StopPos := Pred(AChildren.Count); // the child is moved to the top
|
|
|
|
for I := Succ(ANewPos) to StopPos do
|
|
begin
|
|
Child := TWinControl(AChildren[I]);
|
|
|
|
if Child.HandleAllocated then
|
|
begin
|
|
RefWidget := TCarbonWidget(Child.Handle);
|
|
Order := kHIViewZOrderBelow;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
if RefWidget = nil then Exit;
|
|
end;
|
|
|
|
AChild.SetZOrder(Order, RefWidget);
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.SetCursor
|
|
Params: ACursor - Handle of cursor to set
|
|
|
|
Sets the cursor
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.SetCursor(ACursor: HCURSOR);
|
|
begin
|
|
FCursor := ACursor;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.ScrollBy
|
|
Params: DX, DY
|
|
|
|
Scrolls the content
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.ScrollBy(DX, DY: Integer);
|
|
var
|
|
R: CGRect;
|
|
const
|
|
SName = 'ScrollBy';
|
|
begin
|
|
OSError(HIViewGetBounds(Content, R{%H-}),
|
|
Self, SName, 'HIViewGetBounds');
|
|
OSError(HIViewSetBoundsOrigin(Content, R.origin.x - DX, R.origin.y - DY),
|
|
Self, SName, 'HIViewSetBoundsOrigin');
|
|
with FScrollOffset do
|
|
begin
|
|
X := X + DX;
|
|
Y := Y + DY;
|
|
end;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.ScrollRect
|
|
Params: DX, DY
|
|
|
|
Scrolls the content delimited by a bounding Rect
|
|
------------------------------------------------------------------------------}
|
|
procedure TCarbonWidget.ScrollRect(DX, DY: Integer; ARect: TRect);
|
|
var
|
|
R: CGRect;
|
|
const
|
|
SName = 'ScrollRect';
|
|
begin
|
|
R := RectToCGRect(ARect);
|
|
OSError(HIViewScrollRect(Content, @R, DX, DY),
|
|
Self, SName, 'HIViewScrollRect');
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Method: TCarbonWidget.SetScrollInfo
|
|
Params: SBStyle - Scrollbar type (SB_VERT, SB_HORZ)
|
|
ScrollInfo - Scrolling info
|
|
Returns: The old scroll bar position
|
|
|
|
Sets the scrolling info of the specified scroll bar
|
|
------------------------------------------------------------------------------}
|
|
function TCarbonWidget.SetScrollInfo(SBStyle: Integer;
|
|
const ScrollInfo: TScrollInfo): Integer;
|
|
begin
|
|
Result := 0;
|
|
// ToDo: TCarbonWidget.SetScrollInfo
|
|
DebugLn(ClassName + '.SetScrollInfo unsupported or not implemented!');
|
|
end;
|
|
|
|
initialization
|
|
|
|
LAZARUS_FOURCC := MakeFourCC('Laz ');
|
|
WIDGETINFO_FOURCC := MakeFourCC('WInf');
|
|
MENU_FOURCC := MakeFourCC('Menu');
|
|
WantFreeList:=TFPList.Create;
|
|
|
|
finalization
|
|
|
|
if UPPTree <> nil then FreeAndNil(UPPTree);
|
|
WantFreeList.Free;
|
|
|
|
end.
|