lazarus/lcl/interfaces/customdrawn/customdrawn_cocoaproc.pas

1656 lines
52 KiB
ObjectPascal
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

unit customdrawn_cocoaproc;
{$mode objfpc}{$H+}
{$include customdrawndefines.inc}
interface
uses
// rtl+ftl
Types, Classes, SysUtils, Math,
CGGeometry,
fpimage, fpcanvas,
// Custom Drawn Canvas
IntfGraphics, lazcanvas, customdrawnproc,
// Libs
MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects, LazSysUtils,
//
LMessages, StdCtrls, LCLIntf,
Forms, Controls, LCLMessageGlue, WSControls, LCLType, LCLProc, GraphType;
type
TCocoaForm = objcclass;
TCocoaCustomControl = objcclass;
TCocoaWindow = class(TCDForm)
public
CocoaForm: TCocoaForm;
ClientArea: TCocoaCustomControl;
end;
{ TCocoaForm }
TCocoaForm = objcclass(NSWindow, NSWindowDelegateProtocol)
protected
function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:';
procedure windowWillClose(notification: NSNotification); message 'windowWillClose:';
procedure windowDidBecomeKey(notification: NSNotification); message 'windowDidBecomeKey:';
procedure windowDidResignKey(notification: NSNotification); message 'windowDidResignKey:';
procedure windowDidResize(notification: NSNotification); message 'windowDidResize:';
public
WindowHandle: TCocoaWindow;
function acceptsFirstResponder: Boolean; override;
// Mouse events
procedure mouseUp(event: NSEvent); override;
procedure mouseDown(event: NSEvent); override;
procedure mouseDragged(event: NSEvent); override;
procedure mouseEntered(event: NSEvent); override;
procedure mouseExited(event: NSEvent); override;
procedure mouseMoved(event: NSEvent); override;
// Keyboard events
procedure keyDown(theEvent: NSEvent); override;
procedure keyUp(theEvent: NSEvent); override;
function MacKeyCodeToLCLKey(AKeyEvent: NSEvent; var SendKeyUpDown, SendChar: Boolean; var AUTF8Char: TUTF8Char): Word; message 'MacKeyCodeToLCLKey:sendkey:sendchar:AUTF8Char:';
//
function lclIsVisible: Boolean; message 'lclIsVisible';
procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:';
procedure lclInvalidate; message 'lclInvalidate';
procedure lclLocalToScreen(var X,Y: Integer); message 'lclLocalToScreen::';
function lclFrame: TRect; message 'lclFrame';
procedure lclSetFrame(const r: TRect); message 'lclSetFrame:';
function lclClientFrame: TRect; message 'lclClientFrame';
procedure lclSetClientFrame(const r: TRect); message 'lclSetClientFrame:';
// callback routines
procedure CallbackActivate; message 'CallbackActivate';
procedure CallbackDeactivate; message 'CallbackDeactivate';
procedure CallbackCloseQuery(var CanClose: Boolean); message 'CallbackCloseQuery:';
procedure CallbackResize; message 'CallbackResize';
// Accessibility
function accessibilityAttributeValue(attribute: NSString): id; override;
end;
{ TCocoaCustomControl }
TCocoaCustomControl = objcclass(NSControl)
public
//callback : TCommonCallback;
WindowHandle: TCocoaWindow;
Context : TCocoaContext;
procedure drawRect(dirtyRect: NSRect); override;
procedure Draw(ControlContext: NSGraphicsContext; const Abounds, dirty:NSRect); message 'draw:Context:bounds:';
public
// Keyboard events
function acceptsFirstResponder: Boolean; override;
procedure keyDown(theEvent: NSEvent); override;
procedure keyUp(theEvent: NSEvent); override;
//
function lclInitWithCreateParams(const AParams: TCreateParams): id; message 'lclInitWithCreateParams:';
//
function lclIsVisible: Boolean; message 'lclIsVisible';
procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:';
procedure lclInvalidate; message 'lclInvalidate';
procedure lclLocalToScreen(var X,Y: Integer); message 'lclLocalToScreen::';
function lclParent: id; message 'lclParent';
function lclFrame: TRect; message 'lclFrame';
procedure lclSetFrame(const r: TRect); message 'lclSetFrame:';
function lclClientFrame: TRect; message 'lclClientFrame';
// Accessibility
class function LazRoleToCocoaRole(ALazRole: TLazAccessibilityRole): NSString; message 'LazRoleToCocoaRole:';
function accessibilityAttributeValue(attribute: NSString): id; override;
function accessibilityFocusedUIElement: id; override;
end;
TSelectionInfo = class
public
SelectedText: string;
CaretPos: TPoint;
SelLength: Integer;
SelStart: Integer;
end;
TVisibleTextInfo = class
public
VisibleLineStart, VisibleLineCount: Integer;
// now as a character index in relation to the entire text string
VisibleTextStart, VisibleTextLength: Integer;
end;
{ TCocoaAccessibleObject }
TCocoaAccessibleObject = objcclass(NSObject)
public
// Accessibility
LCLControl: TControl;
LCLAcc: TLazAccessibleObject;
//
LCLInjectedControl: TCustomControl;
LCLBaseControl: TCDBaseControl;
procedure ReadInjectedAndBaseControl; message 'ReadInjectedAndBaseControl';
function GetSelectedTextInfo: TSelectionInfo; message 'GetSelectedTextInfo';
function GetVisibleTextInfo: TVisibleTextInfo; message 'GetVisibleTextInfo';
//NSAccessibilityCategory = objccategory external (NSObject)
function accessibilityAttributeNames: NSArray; override;
function accessibilityAttributeValue(attribute: NSString): id; override;
function accessibilityIsAttributeSettable(attribute: NSString): Boolean; override;
procedure accessibilitySetValue_forAttribute(_value: id; attribute: NSString); override;
function accessibilityParameterizedAttributeNames: NSArray; override;
function accessibilityAttributeValue_forParameter(attribute: NSString; parameter: id): id; override;
function accessibilityActionNames: NSArray; override;
function accessibilityActionDescription(action: NSString): NSString; override;
procedure accessibilityPerformAction(action: NSString); override;
function accessibilityIsIgnored: Boolean; override;
function accessibilityHitTest(point: NSPoint): id; override;
function accessibilityFocusedUIElement: id; override;
{function accessibilityIndexOfChild(child: id): NSUInteger; message 'accessibilityIndexOfChild:';
function accessibilityArrayAttributeCount(attribute: NSString): NSUInteger; message 'accessibilityArrayAttributeCount:';
function accessibilityArrayAttributeValues_index_maxCount(attribute: NSString; index: NSUInteger; maxCount: NSUInteger): NSArray; message 'accessibilityArrayAttributeValues:index:maxCount:';}
end;
{ TCocoaMenu }
TCocoaMenu = objcclass(NSMenu)
public
procedure lclItemSelected(sender: id); message 'lclItemSelected:';
end;
{ TCocoaMenuItem }
TCocoaMenuItem = objcclass(NSMenuItem)
public
procedure lclItemSelected(sender: id); message 'lclItemSelected:';
end;
procedure SetViewDefaults(AView: NSView);
function Cocoa_RawImage_CreateBitmaps(const ARawImage: TRawImage; out ABitmap, AMask: HBitmap; ASkipMask: Boolean): Boolean;
function RawImage_DescriptionToBitmapType(ADesc: TRawImageDescription; out bmpType: TCocoaBitmapType): Boolean;
function CalcNSWindowStyle(const AWinControl: TWinControl; const AParams: TCreateParams): NSUInteger;
function LCLCoordToCocoa(AControl: TControl; ACoord: TPoint): NSPoint;
function CocoaCoordToLCL(AControl: TControl; ACoord: NSPoint): TPoint;
implementation
uses customdrawnwsforms, customdrawnprivate;
(*
About Mac Key codes:
unfortunately, mac key codes are keyboard specific:
that is, there is no universal VK_A, but every keyboard has its code for VK_A
Key codes depend on physical key position on the keyboard: considering a
QWERTY keyboard and an AZERTY one, keycode(Q) of first one = keycode(A) of
the second one, and so on.
For "printable" keys we can rely on kEventParamKeyMacCharCodes and
kEventParamKeyUnicodes event parameters to obtain an ascii/unicode value
that we can translate to the appropriate VK_ code
For non printable keys (Function, ins, arrow and so on...) we use the raw
keycodes, since it looks like they are constant across all keyboards
So, here are constants for non-printable keys (MK means "Mac Key").
These constants were extracted using KeyCodes program by Peter Maurer
(http://www.petermaurer.de/nasi.php?section=keycodes)
Some keys were taken from the ancient "Macintosh Toolbox Essentials", page 87
http://developer.apple.com/documentation/mac/pdf/MacintoshToolboxEssentials.pdf
*)
const
MK_ENTER = $24;
MK_SPACE = $31;
MK_ESC = $35;
MK_F1 = $7A;
MK_F2 = $78;
MK_F3 = $63;
MK_F4 = $76;
MK_F5 = $60;
MK_F6 = $61;
MK_F7 = $62;
MK_F8 = $64;
MK_F9 = $65;
MK_F10 = $6D;
MK_F11 = $67;
MK_F12 = $6F;
MK_F13 = $69; MK_PRNSCR = MK_F13; //Print screen = F13
MK_F14 = $6B; MK_SCRLOCK = MK_F14; //Scroll Lock = F14
MK_F15 = $71; MK_PAUSE = MK_F15; //Pause = F15
MK_POWER = $7F7F;
MK_TAB = $30;
MK_INS = $72; MK_HELP = MK_INS; //old macs call this key "help"
MK_DEL = $75;
MK_HOME = $73;
MK_END = $77;
MK_PAGUP = $74;
MK_PAGDN = $79;
MK_UP = $7E;
MK_DOWN = $7D;
MK_LEFT = $7B;
MK_RIGHT = $7C;
MK_NUMLOCK = $47;
MK_NUMPAD0 = $52;
MK_NUMPAD1 = $53;
MK_NUMPAD2 = $54;
MK_NUMPAD3 = $55;
MK_NUMPAD4 = $56;
MK_NUMPAD5 = $57;
MK_NUMPAD6 = $58;
MK_NUMPAD7 = $59;
MK_NUMPAD8 = $5b;
MK_NUMPAD9 = $5c;
MK_PADEQUALS = $51; //only present in old mac keyboards?
MK_PADDIV = $4B;
MK_PADMULT = $43;
MK_PADSUB = $4E;
MK_PADADD = $45;
MK_PADDEC = $41;
MK_PADENTER = $4C; //enter on numeric keypad
MK_BACKSPACE = $33;
MK_CAPSLOCK = $39;
//Modifiers codes - you'll never get these directly
MK_SHIFTKEY = $38;
MK_CTRL = $3B;
MK_ALT = $3A; MK_OPTION = MK_ALT;
MK_COMMAND = $37; MK_APPLE = MK_COMMAND;
MK_TILDE = 50; // `/~ key
MK_MINUS = 27; // -/_ key
MK_EQUAL = 24; // =/+ key
MK_BACKSLASH = 42; // \ | key
MK_LEFTBRACKET = 33; // [ { key
MK_RIGHTBRACKET = 30; // ] } key
MK_SEMICOLON = 41; // ; : key
MK_QUOTE = 39; // ' " key
MK_COMMA = 43; // , < key
MK_PERIOD = 47; // . > key
MK_SLASH = 44; // / ? key
function Cocoa_RawImage_CreateBitmaps(const ARawImage: TRawImage; out ABitmap, AMask: HBitmap; ASkipMask: Boolean): Boolean;
const
ALIGNMAP: array[TRawImageLineEnd] of TCocoaBitmapAlignment = (cbaByte, cbaByte, cbaWord, cbaDWord, cbaQWord, cbaDQWord);
var
ADesc: TRawImageDescription absolute ARawImage.Description;
bmpType: TCocoaBitmapType;
begin
Result := RawImage_DescriptionToBitmapType(ADesc, bmpType);
if not Result then begin
debugln(['TCarbonWidgetSet.RawImage_CreateBitmaps TODO Depth=',ADesc.Depth,' alphaprec=',ADesc.AlphaPrec,' byteorder=',ord(ADesc.ByteOrder),' alpha=',ADesc.AlphaShift,' red=',ADesc.RedShift,' green=',adesc.GreenShift,' blue=',adesc.BlueShift]);
exit;
end;
ABitmap := HBITMAP(TCocoaBitmap.Create(ADesc.Width, ADesc.Height, ADesc.Depth, ADesc.BitsPerPixel, ALIGNMAP[ADesc.LineEnd], bmpType, ARawImage.Data));
if ASkipMask or (ADesc.MaskBitsPerPixel = 0)
then AMask := 0
else AMask := HBITMAP(TCocoaBitmap.Create(ADesc.Width, ADesc.Height, 1, ADesc.MaskBitsPerPixel, ALIGNMAP[ADesc.MaskLineEnd], cbtMask, ARawImage.Mask));
Result := True;
end;
function RawImage_DescriptionToBitmapType(
ADesc: TRawImageDescription;
out bmpType: TCocoaBitmapType): Boolean;
begin
Result := False;
if ADesc.Format = ricfGray
then
begin
if ADesc.Depth = 1 then bmpType := cbtMono
else bmpType := cbtGray;
end
else if ADesc.Depth = 1
then bmpType := cbtMono
else if ADesc.AlphaPrec <> 0
then begin
if ADesc.ByteOrder = riboMSBFirst
then begin
if (ADesc.AlphaShift = 24)
and (ADesc.RedShift = 16)
and (ADesc.GreenShift = 8 )
and (ADesc.BlueShift = 0 )
then bmpType := cbtARGB
else
if (ADesc.AlphaShift = 0)
and (ADesc.RedShift = 24)
and (ADesc.GreenShift = 16 )
and (ADesc.BlueShift = 8 )
then bmpType := cbtRGBA
else
if (ADesc.AlphaShift = 0 )
and (ADesc.RedShift = 8 )
and (ADesc.GreenShift = 16)
and (ADesc.BlueShift = 24)
then bmpType := cbtBGRA
else Exit;
end
else begin
if (ADesc.AlphaShift = 24)
and (ADesc.RedShift = 16)
and (ADesc.GreenShift = 8 )
and (ADesc.BlueShift = 0 )
then bmpType := cbtBGRA
else
if (ADesc.AlphaShift = 0 )
and (ADesc.RedShift = 8 )
and (ADesc.GreenShift = 16)
and (ADesc.BlueShift = 24)
then bmpType := cbtARGB
else
if (ADesc.AlphaShift = 24 )
and (ADesc.RedShift = 0 )
and (ADesc.GreenShift = 8)
and (ADesc.BlueShift = 16)
then bmpType := cbtRGBA
else Exit;
end;
end
else begin
bmpType := cbtRGB;
end;
Result := True;
end;
function CalcNSWindowStyle(const AWinControl: TWinControl;
const AParams: TCreateParams): NSUInteger;
var
lForm: TCustomForm absolute AWinControl;
begin
case lForm.BorderStyle of
bsNone: Result := NSBorderlessWindowMask;
bsSingle: Result := NSTitledWindowMask or NSClosableWindowMask;
// bsToolWindow should be created by building a NSPanel instead of a NSWindow
bsToolWindow: Result := NSTitledWindowMask or NSClosableWindowMask;
bsSizeToolWin: Result := NSClosableWindowMask or NSResizableWindowMask;
else
Result := NSTitledWindowMask or NSClosableWindowMask or NSResizableWindowMask;
end;
if biMinimize in lForm.BorderIcons then Result := Result or NSMiniaturizableWindowMask;
end;
function LCLCoordToCocoa(AControl: TControl; ACoord: TPoint): NSPoint;
begin
Result.x := ACoord.X;
Result.y := Screen.Height - ACoord.Y;
if AControl <> nil then Result.y := Result.y - AControl.Height;
end;
function CocoaCoordToLCL(AControl: TControl; ACoord: NSPoint): TPoint;
begin
end;
{ TCocoaForm }
function TCocoaForm.windowShouldClose(sender: id): LongBool;
var
canClose : Boolean;
begin
canClose:=true;
CallbackCloseQuery(canClose);
Result:=canClose;
end;
procedure TCocoaForm.windowWillClose(notification: NSNotification);
begin
LCLSendCloseUpMsg(WindowHandle.LCLForm);
end;
procedure TCocoaForm.windowDidBecomeKey(notification: NSNotification);
begin
CallbackActivate;
end;
procedure TCocoaForm.windowDidResignKey(notification: NSNotification);
begin
CallbackDeactivate;
end;
procedure TCocoaForm.windowDidResize(notification: NSNotification);
begin
CallbackResize;
end;
function TCocoaForm.acceptsFirstResponder: Boolean;
begin
Result:=true;
end;
procedure TCocoaForm.mouseUp(event: NSEvent);
var
mp : NSPoint;
begin
mp:=event.locationInWindow;
mp.y:=NSView(event.window.contentView).bounds.size.height-mp.y;
callbackMouseUp(WindowHandle, round(mp.x), round(mp.y), mbLeft);
inherited mouseUp(event);
end;
procedure TCocoaForm.mouseDown(event: NSEvent);
var
mp : NSPoint;
begin
mp:=event.locationInWindow;
mp.y:=NSView(event.window.contentView).bounds.size.height-mp.y;
callbackMouseDown(WindowHandle, round(mp.x), round(mp.y), mbLeft);
inherited mouseDown(event);
end;
procedure TCocoaForm.mouseDragged(event: NSEvent);
var
mp : NSPoint;
begin
mp:=event.locationInWindow;
mp.y:=NSView(event.window.contentView).bounds.size.height-mp.y;
callbackMouseMove(WindowHandle, round(mp.x), round(mp.y));
inherited mouseMoved(event);
end;
procedure TCocoaForm.mouseMoved(event: NSEvent);
var
mp : NSPoint;
begin
mp:=event.locationInWindow;
mp.y:=NSView(event.window.contentView).bounds.size.height-mp.y;
callbackMouseMove(WindowHandle, round(mp.x), round(mp.y));
inherited mouseMoved(event);
end;
procedure TCocoaForm.keyDown(theEvent: NSEvent);
var
lKey: Word;
lUTF8Char: TUTF8Char;
lSendKey, lSendChar: Boolean;
begin
//inherited keyDown(theEvent); Don't call inherited or else Cocoa will think you didn't handle the event and beep on key input
lKey := MacKeyCodeToLCLKey(theEvent, lSendKey, lSendChar, lUTF8Char);
{$ifdef VerboseCDKeyInput}
DebugLn(Format('[TCocoaForm] KeyDown CocoaKeyCode=%x UTF8Char=%s', [theEvent.keyCode(), lUTF8Char]));
{$endif}
if lSendKey then CallbackKeyDown(WindowHandle, lKey);
if lSendChar then CallbackKeyChar(WindowHandle, 0, lUTF8Char);
end;
procedure TCocoaForm.keyUp(theEvent: NSEvent);
var
lKey: Word;
lUTF8Char: TUTF8Char;
lSendKey, lSendChar: Boolean;
begin
//inherited keyUp(theEvent); Don't call inherited or else Cocoa will think you didn't handle the event and beep on key input
lKey := MacKeyCodeToLCLKey(theEvent, lSendKey, lSendChar, lUTF8Char);
if lSendKey then CallbackKeyUp(WindowHandle, lKey);
end;
function TCocoaForm.MacKeyCodeToLCLKey(AKeyEvent: NSEvent; var SendKeyUpDown, SendChar: Boolean; var AUTF8Char: TUTF8Char): Word;
var
KeyCode: Word;
ExtendedKeysSupport: Boolean;
{ TextLen : UInt32;
CharLen : integer;}
characters: string;
charactersIgnoringModifiers: string;
(* widebuf: array[1..2] of widechar;
U: Cardinal;
Layout: UCKeyboardLayoutPtr;
KeyboardLayout: KeyboardLayoutRef;*)
begin
SendKeyUpDown := False;
SendChar := False;
Result := VK_UNKNOWN;
AUTF8Char := '';
ExtendedKeysSupport := Application.ExtendedKeysSupport;
// Obtain data from the event
KeyCode := AKeyEvent.keyCode();
characters := NSStringToString(AKeyEvent.characters());
charactersIgnoringModifiers := NSStringToString(AKeyEvent.charactersIgnoringModifiers());
// IsSysKey:=(GetCurrentEventKeyModifiers and cmdKey)>0;
//non-printable keys (see mackeycodes.inc)
//for these keys, only send keydown/keyup (not char or UTF8KeyPress)
case KeyCode of
MK_F1 : Result:=VK_F1;
MK_F2 : Result:=VK_F2;
MK_F3 : Result:=VK_F3;
MK_F4 : Result:=VK_F4;
MK_F5 : Result:=VK_F5;
MK_F6 : Result:=VK_F6;
MK_F7 : Result:=VK_F7;
MK_F8 : Result:=VK_F8;
MK_F9 : Result:=VK_F9;
MK_F10 : Result:=VK_F10;
MK_F11 : Result:=VK_F11;
MK_F12 : Result:=VK_F12;
{ MK_F13 : Result:=VK_SNAPSHOT;
MK_F14 : Result:=VK_SCROLL;
MK_F15 : Result:=VK_PAUSE;}
MK_POWER : Result:=VK_LCL_POWER;
MK_TAB : Result:=VK_TAB; //strangely enough, tab is "non printable"
MK_INS : Result:=VK_INSERT;
MK_DEL : Result:=VK_DELETE;
MK_HOME : Result:=VK_HOME;
MK_END : Result:=VK_END;
MK_PAGUP : Result:=VK_PRIOR;
MK_PAGDN : Result:=VK_NEXT;
MK_UP : Result:=VK_UP;
MK_DOWN : Result:=VK_DOWN;
MK_LEFT : Result:= VK_LEFT;
MK_RIGHT : Result:= VK_RIGHT;
MK_NUMLOCK : Result:= VK_NUMLOCK;
MK_BACKSPACE: Result:= VK_BACK;
// MK_CAPSLOCK : Result:= VK_CAPSLOCK; <- We don't seam to receive it at all
//Modifiers codes - you'll never get these directly
{MK_SHIFTKEY = $38;
MK_CTRL = $3B;
MK_ALT = $3A; MK_OPTION = MK_ALT;
MK_COMMAND = $37; MK_APPLE = MK_COMMAND;}
end;
if Result<>VK_UNKNOWN then
begin
//stop here, we won't send char or UTF8KeyPress
SendKeyUpDown:=true;
Exit;
end;
// Now other keys which might also send utf8keypress
case KeyCode of
MK_NUMPAD0: Result:=VK_NUMPAD0;
MK_NUMPAD1: Result:=VK_NUMPAD1;
MK_NUMPAD2: Result:=VK_NUMPAD2;
MK_NUMPAD3: Result:=VK_NUMPAD3;
MK_NUMPAD4: Result:=VK_NUMPAD4;
MK_NUMPAD5: Result:=VK_NUMPAD5;
MK_NUMPAD6: Result:=VK_NUMPAD6;
MK_NUMPAD7: Result:=VK_NUMPAD7;
MK_NUMPAD8: Result:=VK_NUMPAD8;
MK_NUMPAD9: Result:=VK_NUMPAD9;
MK_PADDIV : Result:=VK_DIVIDE;
MK_PADMULT : Result:=VK_MULTIPLY;
MK_PADSUB : Result:=VK_SUBTRACT;
MK_PADADD : Result:=VK_ADD;
MK_PADDEC : Result:=VK_DECIMAL;
MK_PADENTER:
begin
Result:=VK_RETURN;
AUTF8Char:=#13;
end;
MK_TILDE: Result := VK_LCL_TILDE; // `/~ key
MK_MINUS: Result := VK_LCL_MINUS;
MK_EQUAL: Result := VK_LCL_EQUAL;
MK_BACKSLASH: Result := VK_LCL_BACKSLASH;
MK_LEFTBRACKET: Result := VK_LCL_OPEN_BRACKET;
MK_RIGHTBRACKET: Result := VK_LCL_CLOSE_BRACKET;
MK_SEMICOLON: Result := VK_LCL_SEMI_COMMA;
MK_QUOTE: Result := VK_LCL_QUOTE;
MK_COMMA: Result := VK_LCL_COMMA;
MK_PERIOD: Result := VK_LCL_POINT;
MK_SLASH: Result := VK_LCL_SLASH;
end;
if Result<>VK_UNKNOWN then SendKeyUpDown:=true;
if AUTF8Char <> '' then
begin
SendChar := True;
Exit;
end;
if Length(charactersIgnoringModifiers) = 1 then
begin
case charactersIgnoringModifiers[1] of
'a'..'z':
begin
Result:=VK_A+ord(charactersIgnoringModifiers[1])-ord('a');
AUTF8Char := charactersIgnoringModifiers;
end;
'A'..'Z':
begin
Result:=ord(charactersIgnoringModifiers[1]);
AUTF8Char := charactersIgnoringModifiers;
end;
#27 : Result:=VK_ESCAPE;
#8 : Result:=VK_BACK;
' ' :
begin
Result:=VK_SPACE;
AUTF8Char := charactersIgnoringModifiers;
end;
#13 : Result:=VK_RETURN;
'0'..'9':
begin
Result:=VK_0+ord(charactersIgnoringModifiers[1])-ord('0');
AUTF8Char := charactersIgnoringModifiers;
end;
end;
end;
if Result<>VK_UNKNOWN then SendKeyUpDown:=true;
if AUTF8Char <> '' then SendChar := True;
if Length(characters) > 1 then
begin
// ToDo: Merge the accents into the UTF-8 character if necessary
AUTF8Char := characters;
//
SendChar := True;
end;
if Result = VK_UNKNOWN then
DebugLn('[MacKeyCodeToLCLKey] Unknown Key! KeyCode=%d characters=%s charactersIgnoringModifiers=%s',
[KeyCode, characters, charactersIgnoringModifiers]);
end;
procedure TCocoaForm.mouseEntered(event: NSEvent);
begin
inherited mouseEntered(event);
end;
procedure TCocoaForm.mouseExited(event: NSEvent);
begin
inherited mouseExited(event);
end;
function TCocoaForm.lclIsVisible:Boolean;
begin
Result:=isVisible;
end;
procedure TCocoaForm.lclInvalidateRect(const r:TRect);
begin
contentView.lclInvalidateRect(r);
end;
procedure TCocoaForm.lclInvalidate;
begin
contentView.lclInvalidate;
end;
procedure TCocoaForm.lclLocalToScreen(var X,Y:Integer);
var
f : NSRect;
begin
if Assigned(screen) then begin
f:=frame;
x:=Round(f.origin.x+x);
y:=Round(screen.frame.size.height-f.size.height-f.origin.y);
end;
end;
function TCocoaForm.lclFrame:TRect;
begin
if Assigned(screen)
then NSToLCLRect(frame, screen.frame.size.height, Result)
else NSToLCLRect(frame, Result);
end;
procedure TCocoaForm.lclSetFrame(const r:TRect);
var
ns: NSRect;
begin
if Assigned(screen)
then LCLToNSRect(r, screen.frame.size.height, ns)
else LCLToNSRect(r, ns);
setFrame_display(ns, isVisible);
end;
function TCocoaForm.lclClientFrame:TRect;
var
wr : NSRect;
b : CGGeometry.CGRect;
begin
wr:=frame;
b:=contentView.frame;
Result.Left:=Round(b.origin.x);
Result.Top:=Round(wr.size.height-b.origin.y);
Result.Right:=Round(b.origin.x+b.size.width);
Result.Bottom:=Round(Result.Top+b.size.height);
end;
procedure TCocoaForm.lclSetClientFrame(const r: TRect);
var
ns, lAdjustedRect: NSRect;
lTitleBarHeight: Single;
begin
if Assigned(screen)
then LCLToNSRect(r, screen.frame.size.height, ns)
else LCLToNSRect(r, ns);
// LCL coordinates are for the client area of the Window, while setFrame_display
// includes the title bar too, so we need to consider this here, but in a clever
// way, as to keep the window position the same
lAdjustedRect := frameRectForContentRect(ns);
lTitleBarHeight := lAdjustedRect.size.height - ns.size.height;
ns.origin.y := ns.origin.y - lTitleBarHeight;
ns.size.height := lAdjustedRect.size.height;
setFrame_display(ns, isVisible);
end;
procedure TCocoaForm.CallbackActivate;
begin
LCLSendActivateMsg(WindowHandle.LCLForm, WA_ACTIVE, false);
end;
procedure TCocoaForm.CallbackDeactivate;
begin
LCLSendActivateMsg(WindowHandle.LCLForm, WA_INACTIVE, false);
end;
procedure TCocoaForm.CallbackCloseQuery(var CanClose: Boolean);
begin
// Message results : 0 - do nothing, 1 - destroy window
CanClose:=LCLSendCloseQueryMsg(WindowHandle.LCLForm)>0;
end;
procedure TCocoaForm.CallbackResize;
var
sz : NSSize;
r : TRect;
begin
sz := frame.size;
TCDWSCustomForm.GetClientBounds(TWinControl(WindowHandle.LCLForm), r);
if Assigned(WindowHandle.LCLForm) then
LCLSendSizeMsg(WindowHandle.LCLForm, Round(sz.width), Round(sz.height), SIZENORMAL);
end;
function TCocoaForm.accessibilityAttributeValue(attribute: NSString): id;
var
lStrAttr: String;
lAResult: NSArray;
lMAResult: NSMutableArray;
begin
Result := inherited accessibilityAttributeValue(attribute);
{$ifdef VerboseCDAccessibility}
//lStrAttr := NSStringToString(attribute);
//DebugLn('[TCocoaCustomControl.accessibilityAttributeValue] attribute='+lStrAttr);
{$endif}
// Cocoa by default merges the window and it's main subcontrol into 1 things
// And Finder and other Cocoa apps follow this, so let's do it like that too
//
// If we want to put all items inside a sub-window in the window then we could
// activate the code bellow, but then we have to solve parent missmatch errors which we get
//
// Curiously, even while not being in the list of elements,
// TCocoaCustomControl.accessibilityAttributeValue is executed
// as if it spoke for the window and not for itself, this adding the sub-window
// duplicates all out accessibility entries (appear once for the window and once for the sub-window)
(* if attribute.isEqualToString(NSAccessibilityChildrenAttribute) then
begin
// Strangely Cocoa doesn't add automatically the client area to the array
{$ifdef VerboseCDAccessibility}
//DebugLn('[TCocoaForm.accessibilityAttributeValue] NSAccessibilityChildrenAttribute');
{$endif}
lAResult := NSArray(Result);
lMAResult := lAResult.mutableCopy();
lMAResult.addObject(WindowHandle.ClientArea);
Result := lMAResult;
end; *)
end;
{ TCocoaCustomControl }
procedure TCocoaCustomControl.drawRect(dirtyRect:NSRect);
begin
inherited drawRect(dirtyRect);
Draw(NSGraphicsContext.currentContext, bounds, dirtyRect);
end;
procedure TCocoaCustomControl.Draw(ControlContext: NSGraphicsContext;
const Abounds, dirty:NSRect);
var
lWidth, lHeight: Integer;
lBitmap, lMask: HBITMAP;
lRawImage: TRawImage;
AImage: TLazIntfImage;
ACanvas: TLazCanvas;
{$IFDEF VerboseCDPaintProfiler}
lTimeStart, lNativeStart: TDateTime;
{$ENDIF}
begin
{$IFDEF VerboseCDPaintProfiler}
lTimeStart := NowUTC();
{$ENDIF}
if not Assigned(Context) then Context:=TCocoaContext.Create;
Context.ctx:=ControlContext;
lWidth := Round(bounds.size.width);
lHeight := Round(bounds.size.height);
if Context.InitDraw(lWidth, lHeight) then
begin
// Prepare the non-native image and canvas
UpdateControlLazImageAndCanvas(WindowHandle.Image,
WindowHandle.Canvas, lWidth, lHeight, clfRGB24UpsideDown);
DrawFormBackground(WindowHandle.Image, WindowHandle.Canvas, WindowHandle.LCLForm);
WindowHandle.Canvas.NativeDC := PtrInt(Context);
// Draw the form
RenderForm(WindowHandle.Image, WindowHandle.Canvas, WindowHandle.LCLForm);
{$IFDEF VerboseCDPaintProfiler}
lNativeStart := NowUTC();
{$ENDIF}
// Now render it into the control
WindowHandle.Image.GetRawImage(lRawImage);
Cocoa_RawImage_CreateBitmaps(lRawImage, lBitmap, lMask, True);
Context.DrawBitmap(0, 0, TCocoaBitmap(lBitmap));
end;
{$IFDEF VerboseCDPaintProfiler}
DebugLn(Format('[TCocoaCustomControl.Draw] Paint LCL-CustomDrawn: %d ms Native: %d ms',
[DateTimeToMilliseconds(lNativeStart - lTimeStart),
DateTimeToMilliseconds(NowUTC() - lNativeStart)]));
{$ENDIF}
end;
function TCocoaCustomControl.acceptsFirstResponder: Boolean;
begin
Result := True;
end;
procedure TCocoaCustomControl.keyDown(theEvent: NSEvent);
begin
//DebugLn('[TCocoaCustomControl] KeyDown='+IntToHex(theEvent.keyCode(), 4));
WindowHandle.CocoaForm.keyDown(theEvent);
end;
procedure TCocoaCustomControl.keyUp(theEvent: NSEvent);
begin
WindowHandle.CocoaForm.keyUp(theEvent);
end;
function RectToViewCoord(view: NSView; const r: TRect): NSRect;
var
b: NSRect;
begin
if not Assigned(view) then Exit;
b:=view.bounds;
Result.origin.x:=r.Left;
Result.origin.y:=b.size.height-r.Top;
Result.size.width:=r.Right-r.Left;
Result.size.height:=r.Bottom-r.Top;
end;
function TCocoaCustomControl.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;
function TCocoaCustomControl.lclIsVisible:Boolean;
begin
Result:=not isHidden;
end;
procedure TCocoaCustomControl.lclInvalidateRect(const r:TRect);
begin
setNeedsDisplayInRect(RectToViewCoord(Self, r));
end;
procedure TCocoaCustomControl.lclInvalidate;
begin
setNeedsDisplay_(True);
end;
procedure TCocoaCustomControl.lclLocalToScreen(var X,Y:Integer);
begin
end;
function TCocoaCustomControl.lclParent:id;
begin
Result:=superView;
end;
function TCocoaCustomControl.lclFrame: TRect;
var
v : NSView;
begin
v:=superview;
if Assigned(v)
then NSToLCLRect(frame, v.frame.size.height, Result)
else NSToLCLRect(frame, Result);
end;
procedure TCocoaCustomControl.lclSetFrame(const r:TRect);
var
ns : NSRect;
begin
if Assigned(superview)
then LCLToNSRect(r, superview.frame.size.height, ns)
else LCLToNSRect(r, ns);
setFrame(ns);
end;
function TCocoaCustomControl.lclClientFrame:TRect;
var
r: NSRect;
begin
r:=bounds;
Result.Left:=0;
Result.Top:=0;
Result.Right:=Round(r.size.width);
Result.Bottom:=Round(r.size.height);
end;
class function TCocoaCustomControl.LazRoleToCocoaRole(
ALazRole: TLazAccessibilityRole): NSString;
begin
case ALazRole of
larAnimation: Result := NSAccessibilityImageRole;
larButton: Result := NSAccessibilityButtonRole;
larCell: Result := NSAccessibilityCellRole;
larChart: Result := NSAccessibilityImageRole;
larCheckBox: Result := NSAccessibilityCheckBoxRole;
larClock: Result := NSAccessibilityStaticTextRole;
larColorPicker: Result := NSAccessibilityColorWellRole;
larComboBox: Result := NSAccessibilityComboBoxRole;
larDateField: Result := NSAccessibilityStaticTextRole;//('AXDateField');
larGrid: Result := NSAccessibilityGridRole;
larGroup: Result := NSAccessibilityGroupRole;
larImage: Result := NSAccessibilityImageRole;
larLabel: Result := NSAccessibilityStaticTextRole;
larListBox: Result := NSAccessibilityListRole;
larListItem: Result := NSAccessibilityStaticTextRole;
larMenuBar: Result := NSAccessibilityMenuBarRole;
larMenuItem: Result := NSAccessibilityMenuItemRole;
larProgressIndicator: Result := NSAccessibilityProgressIndicatorRole;
larRadioButton: Result := NSAccessibilityRadioButtonRole;
larResizeGrip: Result := NSAccessibilityStaticTextRole; // VoiceOver cannot handle AXHandle in Mac 10.6, so we fallback to AXStaticText
larScrollBar: Result := NSAccessibilityScrollBarRole;
larSpinner: Result := NSAccessibilityIncrementorRole;
larTabControl: Result := NSAccessibilityTabGroupRole;
larTextEditorMultiline: Result := NSAccessibilityTextAreaRole;
larTextEditorSingleline: Result := NSAccessibilityTextFieldRole;
// NSAccessibilityToolbarRole: NSString; cvar; external;
larTrackBar: Result := NSAccessibilitySliderRole;
larTreeView: Result := NSAccessibilityListRole;
larTreeItem: Result := NSAccessibilityStaticTextRole;
larWindow: Result := NSAccessibilityWindowRole;//NSAccessibilityUnknownRole;
else
Result := NSAccessibilityUnknownRole;
end;
end;
function TCocoaCustomControl.accessibilityAttributeValue(attribute: NSString): id;
var
lStrAttr: String;
lAResult: NSArray;
lMAResult: NSMutableArray;
i: Integer;
lForm: TCustomForm;
lFormAcc, lChildAcc: TLazAccessibleObject;
lAccObject: TCocoaAccessibleObject;
begin
Result := inherited accessibilityAttributeValue(attribute);
lStrAttr := NSStringToString(attribute);
//DebugLn('[TCocoaCustomControl.accessibilityAttributeValue] attribute='+lStrAttr);
if attribute.isEqualToString(NSAccessibilityRoleAttribute) then
begin
{$ifdef VerboseCDAccessibility}
DebugLn('[TCocoaCustomControl.accessibilityAttributeValue] NSAccessibilityRoleAttribute');
{$endif}
lForm := WindowHandle.LCLForm;
lFormAcc := lForm.GetAccessibleObject();
Result := LazRoleToCocoaRole(lFormAcc.AccessibleRole);
end
else if attribute.isEqualToString(NSAccessibilityChildrenAttribute) then
begin
{$ifdef VerboseCDAccessibility}
lStrAttr := '[TCocoaCustomControl.accessibilityAttributeValue] NSAccessibilityChildrenAttribute';
{$endif}
lAResult := NSArray(Result);
lMAResult := NSMutableArray.arrayWithCapacity(0); //lAResult.mutableCopy();
lForm := WindowHandle.LCLForm;
lFormAcc := lForm.GetAccessibleObject();
lChildAcc := lFormAcc.GetFirstChildAccessibleObject();
while lChildAcc <> nil do
begin
lAccObject := TCocoaAccessibleObject(lChildAcc.Handle);
if lAccObject <> nil then
begin
lMAResult.addObject(lAccObject);
{$ifdef VerboseCDAccessibility}
lStrAttr := lStrAttr + Format(' Adding object %s : %x', [lChildAcc.OwnerControl.ClassName, PtrUInt(lAccObject)]);
{$endif}
end;
lChildAcc := lFormAcc.GetNextChildAccessibleObject();
end;
{$ifdef VerboseCDAccessibility}
DebugLn(lStrAttr);
{$endif}
Result := lMAResult;
end;
end;
function TCocoaCustomControl.accessibilityFocusedUIElement: id;
var
lForm: TCustomForm;
// lFormAcc: TLazAccessibleObject;
lActiveControl: TWinControl;
begin
Result := inherited accessibilityFocusedUIElement();
lForm := WindowHandle.LCLForm;
// lFormAcc := lForm.GetAccessibleObject();
lActiveControl := lForm.ActiveControl;
if lActiveControl = nil then lActiveControl := lForm;
Result := id(lActiveControl.GetAccessibleObject().Handle);
end;
{ TCocoaAccessibleObject }
procedure TCocoaAccessibleObject.ReadInjectedAndBaseControl;
var
lHandle: TCDWinControl;
begin
if not (LCLControl is TWinControl) then Exit;
if LCLBaseControl = nil then
begin
lHandle := TCDWinControl(TWinControl(LCLControl).Handle);
LCLBaseControl := lHandle;
if lHandle <> nil then
LCLInjectedControl := lHandle.CDControl
else
LCLInjectedControl := nil;
end;
end;
function TCocoaAccessibleObject.GetSelectedTextInfo: TSelectionInfo;
var
lCustomEdit: TCustomEdit;
begin
// initialization
Result := TSelectionInfo.Create;
if LCLControl is TCustomEdit then
begin
lCustomEdit := TCustomEdit(LCLControl);
Result.SelectedText := lCustomEdit.SelText;
Result.CaretPos := lCustomEdit.CaretPos;
Result.SelLength := lCustomEdit.SelLength;
Result.SelStart := lCustomEdit.SelStart;
end;
end;
function TCocoaAccessibleObject.GetVisibleTextInfo: TVisibleTextInfo;
var
lCustomMemo: TCustomMemo;
lCustomEdit: TCustomEdit;
lDC: HDC;
lOldFont: HFont;
lTM: TTextMetric;
lRect: TRect;
lLines: TStrings = nil;
lScrollBar: TControlScrollbar = nil;
i: Integer;
lScrollPos, lScrollRange, lScrollPage: Integer;
lStr: String;
begin
// initialization
Result := TVisibleTextInfo.Create;
Result.VisibleLineCount := 1;
if LCLControl is TCustomMemo then
begin
lCustomMemo := TCustomMemo(LCLControl);
lDC := LCLIntf.GetDC(lCustomMemo.Handle);
lOldFont := SelectObject(lDC, lCustomMemo.Font.Handle);
try
GetTextMetrics(lDC, lTM);
lRect := LCLControl.ClientRect;
Result.VisibleLineCount := (lRect.Bottom - lRect.Top) div
(lTM.tmHeight + lTM.tmExternalLeading);
finally
SelectObject(lDC, lOldFont);
ReleaseDC(lCustomMemo.Handle, lDC);
end;
lLines := lCustomMemo.Lines;
lScrollBar := lCustomMemo.VertScrollBar;
end;
// With a TStrings and a ScrollBar we can get the visible range info
if (lLines <> nil) and (lScrollBar <> nil) then
begin
lScrollPos := lScrollBar.Position;
lScrollRange := lScrollBar.Range;
lScrollPage := lScrollBar.Page;
Result.VisibleLineStart := Round((lScrollPos / lScrollRange) * lLines.Count);
Result.VisibleLineCount := Round((lScrollPage / lScrollRange) * lLines.Count);
Result.VisibleLineCount := Min(Result.VisibleLineCount, lLines.Count - 1 - Result.VisibleLineStart); // sanity correction
// using the calculated info get the character indexes
for i := 0 to Result.VisibleLineStart + Result.VisibleLineCount do
begin
if i < Result.VisibleLineStart then
begin
lStr := lLines.Strings[i];
Result.VisibleTextStart := Result.VisibleTextStart + UTF8Length(lStr) + 1; // +1 for the Mac line ending
end
else
begin
lStr := lLines.Strings[i];
Result.VisibleTextLength := Result.VisibleTextLength + UTF8Length(lStr) + 1; // +1 for the Mac line ending
end;
end;
end;
end;
{
See the documentation about accessibility Roles and their required attributes:
http://developer.apple.com/library/mac/documentation/UserExperience/Reference/Accessibility_RoleAttribute_Ref/Role.html
}
function TCocoaAccessibleObject.accessibilityAttributeNames: NSArray;
var
lResult: NSMutableArray;
lCocoaRole: NSString;
// Basic elements which most roles have
procedure AddBasicAttributes;
begin
lResult.addObject(NSAccessibilityDescriptionAttribute);
lResult.addObject(NSAccessibilityEnabledAttribute);
lResult.addObject(NSAccessibilityFocusedAttribute);
lResult.addObject(NSAccessibilityParentAttribute);
lResult.addObject(NSAccessibilityPositionAttribute);
lResult.addObject(NSAccessibilityRoleAttribute);
lResult.addObject(NSAccessibilitySizeAttribute);
lResult.addObject(NSAccessibilityTitleAttribute);
lResult.addObject(NSAccessibilityTopLevelUIElementAttribute);
lResult.addObject(NSAccessibilityWindowAttribute);
end;
begin
{$ifdef VerboseCDAccessibility}
DebugLn(Format('[TCocoaAccessibleObject.accessibilityAttributeNames] Self=%x', [PtrUInt(Self)]));
{$endif}
lResult := NSMutableArray.array_();
lCocoaRole := TCocoaCustomControl.LazRoleToCocoaRole(LCLAcc.AccessibleRole);
//larAnimation
//larButton
if lCocoaRole.isEqualToString(NSAccessibilityButtonRole) then
begin
AddBasicAttributes();
lResult.addObject(NSAccessibilityRoleDescriptionAttribute);
end
//larCell
//larChart
//larCheckBox
//larClock
//larColorPicker
//larComboBox
//larDateField
//larGrid
//larGroup
//larImage
//larLabel
else if lCocoaRole.isEqualToString(NSAccessibilityStaticTextRole) then
begin
AddBasicAttributes();
lResult.addObject(NSAccessibilityValueAttribute);
end
//larListBox
//larListItem
//larMenuBar
//larMenuItem
//larProgressIndicator
//larRadioButton
//larResizeGrip
//larScrollBar
//larSpinner
//larTabControl
//larTextEditorMultiline
//larTextEditorSingleline
else if lCocoaRole.isEqualToString(NSAccessibilityTextFieldRole) then
begin
AddBasicAttributes();
lResult.addObject(NSAccessibilityNumberOfCharactersAttribute); // Number of characters in an editable text field. Must not be settable.
lResult.addObject(NSAccessibilitySelectedTextAttribute); // Currently selected text of a UI element. May be settable.
lResult.addObject(NSAccessibilitySelectedTextRangeAttribute); // Position and length (in characters) of a selected portion of text in the UI element. May be settable.
lResult.addObject(NSAccessibilityValueAttribute); // The elements value. May be settable.
lResult.addObject(NSAccessibilityVisibleCharacterRangeAttribute); // Range of characters that are scrolled into view in an editable text element. May be settable.
end
//larTrackBar
//larTreeView
//larTreeItem
//larWindow
else
begin
AddBasicAttributes();
end;
// This one we use to put LCL object and class names to help debugging =)
lResult.addObject(NSAccessibilityUnitDescriptionAttribute);
Result := lResult;
end;
// Note that if there is an injected control we will send information about it instead
// of about the host control
function TCocoaAccessibleObject.accessibilityAttributeValue(attribute: NSString): id;
var
lStrAttr: String;
lTmpStr: string;
//
lSize: TSize;
lPoint: TPoint;
lBool: Boolean;
lInt: Integer;
//
lAResult: NSArray;
lMAResult: NSMutableArray;
lNSSize: NSSize;
lNSPoint: NSPoint;
lNSRange: NSRange;
//
i: Integer;
lChildAcc: TLazAccessibleObject;
lForm: TCustomForm;
lParent: TWinControl;
lSelInfo: TSelectionInfo;
lVisibleTextInfo: TVisibleTextInfo;
function GetControlText: string;
begin
Result := LCLControl.Caption;
end;
begin
//Result := inherited accessibilityAttributeValue(attribute); -> This raises errors, so don't
Result := nil;
ReadInjectedAndBaseControl();
lStrAttr := NSStringToString(attribute);
{$ifdef VerboseCDAccessibility}
DebugLn(Format(':>[TCocoaAccessibleObject.accessibilityAttributeValue] Self=%x LCLControl=%x',
[PtrUInt(Self), PtrUInt(LCLControl)]));
{$endif}
//
// Role
//
if attribute.isEqualToString(NSAccessibilityRoleAttribute) then
begin
{$ifdef VerboseCDAccessibility}
DebugLn(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilityRoleAttribute');
{$endif}
Result := TCocoaCustomControl.LazRoleToCocoaRole(LCLAcc.AccessibleRole);
end
//
// RoleDescription
//
else if attribute.isEqualToString(NSAccessibilityRoleDescriptionAttribute) then
begin
Result := NSStringUtf8(LCLControl.Caption);
end
//
// Value
//
// The elements value. May be settable.
//
else if attribute.isEqualToString(NSAccessibilityValueAttribute) then
begin
Result := NSStringUtf8(LCLControl.Caption);
end
{else if attribute = NSAccessibilityMinValueAttribute: NSString; cvar; external;
NSAccessibilityMaxValueAttribute: NSString; cvar; external;}
//
// Enabled
//
else if attribute.isEqualToString(NSAccessibilityEnabledAttribute) then
begin
Result := NSNumber.numberWithBool(LCLControl.Enabled);
end
//
// Focused
//
else if attribute.isEqualToString(NSAccessibilityFocusedAttribute) then
begin
if LCLControl is TWinControl then
begin
lBool := TWinControl(LCLControl).Focused;
if (not lBool) and (LCLInjectedControl <> nil) then
lBool := LCLInjectedControl.Focused;
Result := NSNumber.numberWithBool(lBool);
end
else Result := NSNumber.numberWithBool(False);
end
//
// Parent
//
else if attribute.isEqualToString(NSAccessibilityParentAttribute) or
attribute.isEqualToString(NSAccessibilityTopLevelUIElementAttribute) then
begin
lParent := LCLControl.Parent;
if lParent <> nil then
begin
{$ifdef VerboseCDAccessibility}
DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilityParentAttribute Self=%s Parent=%s',
[LCLControl.ClassName, lParent.ClassName]));
{$endif}
// if the parent is a form, pass to the custom control
if lParent is TCustomForm then
begin
Result := TCocoaWindow(lParent.Handle).CocoaForm;//ClientArea;
{$ifdef VerboseCDAccessibility}
DebugLn(':<[TCocoaAccessibleObject.accessibilityAttributeValue] Parent is TCustomForm');
{$endif}
end
else
Result := TCocoaAccessibleObject(lParent.GetAccessibleObject().Handle);
end
else
begin
{$ifdef VerboseCDAccessibility}
DebugLn(':<[TCocoaAccessibleObject.accessibilityAttributeValue] ERROR: Parent is nil!');
{$endif}
Result := nil;
end;
end
//
// Children
//
else if attribute.isEqualToString(NSAccessibilityChildrenAttribute)
or attribute.isEqualToString(NSAccessibilityVisibleChildrenAttribute) then
begin
{$ifdef VerboseCDAccessibility}
DebugLn(':[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilityChildrenAttribute');
{$endif}
lAResult := NSArray(Result);
lMAResult := lAResult.mutableCopy();
//if Self.LCLInjectedControl = nil then
for i := 0 to LCLAcc.GetChildAccessibleObjectsCount() - 1 do
begin
lChildAcc := LCLAcc.GetChildAccessibleObject(i);
lMAResult.addObject(TCocoaAccessibleObject(lChildAcc.Handle));
end;
Result := lMAResult;
end
//
// Window
//
else if attribute.isEqualToString(NSAccessibilityWindowAttribute) then
begin
lForm := Forms.GetParentForm(LCLControl);
//Result := TCocoaAccessibleObject(lForm.GetAccessibleObject().Handle);
Result := TCocoaWindow(lForm.Handle).CocoaForm;//ClientArea;
end
else if attribute.isEqualToString(NSAccessibilitySelectedChildrenAttribute) then
begin
end
//
// Position is in screen coordinates!
//
else if attribute.isEqualToString(NSAccessibilityPositionAttribute) then
begin
lPoint := LCLControl.ClientToScreen(Types.Point(0, 0));
lNSPoint := LCLCoordToCocoa(LCLControl, lPoint);
Result := NSValue.valueWithPoint(lNSPoint);
{$ifdef VerboseCDAccessibility}
DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilityPositionAttribute Result=%d,%d', [lPoint.X, lPoint.Y]));
{$endif}
end
//
// Size
//
else if attribute.isEqualToString(NSAccessibilitySizeAttribute) then
begin
lSize := LCLAcc.Size;
lNSSize.width := lSize.CX;
lNSSize.height := lSize.CY;
Result := NSValue.valueWithSize(lNSSize);
{$ifdef VerboseCDAccessibility}
DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilitySizeAttribute Result=%d,%d', [lSize.CX, lSize.CY]));
{$endif}
end
//
// NumberOfCharacters
//
// Number of characters in an editable text field. Must not be settable.
//
else if attribute.isEqualToString(NSAccessibilityNumberOfCharactersAttribute) then
begin
lTmpStr := LCLControl.Caption;
lInt := UTF8Length(lTmpStr);
Result := NSNumber.numberWithInt(lInt);
{$ifdef VerboseCDAccessibility}
DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilityNumberOfCharactersAttribute Text=%s Result=%d', [lTmpStr, lInt]));
{$endif}
end
//
// SelectedText
//
// Currently selected text of a UI element. May be settable.
//
else if attribute.isEqualToString(NSAccessibilitySelectedTextAttribute) then
begin
lSelInfo := GetSelectedTextInfo();
try
Result := NSStringUtf8(lSelInfo.SelectedText);
finally
lSelInfo.Free;
end;
//{$ifdef VerboseCDAccessibility}
//DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilitySizeAttribute Result=%d,%d', [lSize.CX, lSize.CY]));
//{$endif}
end
//
// SelectedTextRange
//
// Position and length (in characters) of a selected portion of text in the UI element. May be settable.
//
else if attribute.isEqualToString(NSAccessibilitySelectedTextRangeAttribute) then
begin
lSelInfo := GetSelectedTextInfo();
try
lNSRange.location := lSelInfo.SelStart;
lNSRange.length := lSelInfo.SelLength;
Result := NSValue.valueWithRange(lNSRange);
finally
lSelInfo.Free;
end;
//{$ifdef VerboseCDAccessibility}
//DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilitySizeAttribute Result=%d,%d', [lSize.CX, lSize.CY]));
//{$endif}
end
//
// VisibleCharacterRange
//
// Range of characters that are scrolled into view in an editable text element. May be settable.
//
else if attribute.isEqualToString(NSAccessibilityVisibleCharacterRangeAttribute) then
begin
lVisibleTextInfo := GetVisibleTextInfo();
try
lNSRange.location := lVisibleTextInfo.VisibleTextStart;
lNSRange.length := lVisibleTextInfo.VisibleTextLength;
Result := NSValue.valueWithRange(lNSRange);
finally
lVisibleTextInfo.Free;
end;
//{$ifdef VerboseCDAccessibility}
//DebugLn(Format(':<[TCocoaAccessibleObject.accessibilityAttributeValue] NSAccessibilityVisibleCharacterRangeAttribute Result=%d,%d', [lSize.CX, lSize.CY]));
//{$endif}
end
//
// This one we use to put LCL object and class names to help debugging =)
//
else if attribute.isEqualToString(NSAccessibilityUnitDescriptionAttribute) then
begin
Result := NSStringUtf8(LCLControl.Name+':'+LCLControl.ClassName);
end;
end;
function TCocoaAccessibleObject.accessibilityIsAttributeSettable(
attribute: NSString): Boolean;
begin
{$ifdef VerboseCDAccessibility}
DebugLn('[TCocoaAccessibleObject.accessibilityIsAttributeSettable]');
{$endif}
Result := False;
//
// Value - The elements value. May be settable.
//
if attribute.isEqualToString(NSAccessibilityValueAttribute) then
begin
if LCLAcc.AccessibleRole in [larComboBox, larTextEditorMultiline, larTextEditorSingleline] then
begin
Result := True;
end;
end
//
// Always Settable attributes
//
else if attribute.isEqualToString(NSAccessibilityFocusedAttribute) or
attribute.isEqualToString(NSAccessibilitySelectedTextAttribute) or
attribute.isEqualToString(NSAccessibilitySelectedTextRangeAttribute) or
attribute.isEqualToString(NSAccessibilityVisibleCharacterRangeAttribute) then
begin
Result := True;
end;
end;
procedure TCocoaAccessibleObject.accessibilitySetValue_forAttribute(_value: id;
attribute: NSString);
begin
{$ifdef VerboseCDAccessibility}
DebugLn(':>[TCocoaAccessibleObject.accessibilitySetValue_forAttribute]');
{$endif}
if attribute.isEqualToString(NSAccessibilityFocusedAttribute) then
begin
{$ifdef VerboseCDAccessibility}
DebugLn(':<[TCocoaAccessibleObject.accessibilitySetValue_forAttribute] Control=%s:%s Value=%d',
[LCLControl.Name, LCLControl.ClassName, NSNumber(value).intValue()]);
{$endif}
if not (LCLControl is TWinControl) then Exit;
if not NSNumber(value).boolValue() then Exit;
if LCLInjectedControl <> nil then LCLInjectedControl.SetFocus()
else TWinControl(LCLControl).SetFocus();
end
else if attribute.isEqualToString(NSAccessibilityValueAttribute) then
begin
LCLControl.Caption := NSStringToString(value);
end;
end;
function TCocoaAccessibleObject.accessibilityParameterizedAttributeNames: NSArray;
var
lResult: NSMutableArray;
lCocoaRole: NSString;
begin
lResult := NSMutableArray.array_();
lCocoaRole := TCocoaCustomControl.LazRoleToCocoaRole(LCLAcc.AccessibleRole);
Result := lResult;
end;
function TCocoaAccessibleObject.accessibilityAttributeValue_forParameter(
attribute: NSString; parameter: id): id;
begin
{$ifdef VerboseCDAccessibility}
DebugLn('[TCocoaAccessibleObject.accessibilityAttributeValue_forParameter]');
{$endif}
Result := nil;
end;
function TCocoaAccessibleObject.accessibilityActionNames: NSArray;
var
lResult: NSMutableArray;
lCocoaRole: NSString;
begin
{$ifdef VerboseCDAccessibility}
DebugLn('[TCocoaAccessibleObject.accessibilityActionNames]');
{$endif}
lResult := NSMutableArray.array_();
lCocoaRole := TCocoaCustomControl.LazRoleToCocoaRole(LCLAcc.AccessibleRole);
if lCocoaRole.caseInsensitiveCompare(NSAccessibilityButtonRole) = NSOrderedSame then
begin
lResult.addObject(NSAccessibilityPressAction);
end;
Result := lResult;
end;
function TCocoaAccessibleObject.accessibilityActionDescription(action: NSString): NSString;
begin
if action = NSAccessibilityPressAction then Result := NSSTR('Press')
else Result := NSSTR('');
end;
procedure TCocoaAccessibleObject.accessibilityPerformAction(action: NSString);
begin
end;
function TCocoaAccessibleObject.accessibilityIsIgnored: Boolean;
begin
Result := LCLAcc.AccessibleRole = larIgnore;
end;
function TCocoaAccessibleObject.accessibilityHitTest(point: NSPoint): id;
begin
Result := inherited accessibilityHitTest(point);
end;
function TCocoaAccessibleObject.accessibilityFocusedUIElement: id;
begin
Result := inherited accessibilityFocusedUIElement;
end;
{ TCocoaMenu }
procedure TCocoaMenu.lclItemSelected(sender: id);
begin
end;
{ TCocoaMenuItem }
procedure TCocoaMenuItem.lclItemSelected(sender: id);
begin
end;
procedure SetViewDefaults(AView:NSView);
begin
if not Assigned(AView) then Exit;
AView.setAutoresizingMask(NSViewMinYMargin or NSViewMaxXMargin);
end;
end.