1
0
mirror of https://github.com/salvadordf/CEF4Delphi.git synced 2025-08-04 21:32:54 +02:00

Added partial keyboard support to FMXExternalPumpBrowser for MacOS

Added FMXExternalPumpBrowser demo for Linux thanks to Christoph Schneider
This commit is contained in:
Salvador Díaz Fau
2021-05-24 18:42:12 +02:00
parent 51a29225ef
commit a22e1a07b1
15 changed files with 3149 additions and 341 deletions

View File

@ -86,7 +86,6 @@ type
private
FNewDelegate : ICrAppControlProtocol;
FOldDelegate : NSApplicationDelegate;
FLastMacOsKeyDownCode : integer;
protected
class var OldFMXApplicationService: IFMXApplicationService;
@ -122,7 +121,6 @@ type
function applicationDockMenu(sender: NSApplication): NSMenu;
class procedure AddPlatformService;
class function LastMacOsKeyDownCode: integer;
property DefaultTitle : string read GetDefaultTitle;
property Title : string read GetTitle write SetTitle;
@ -201,7 +199,6 @@ begin
FNewDelegate := nil;
FOldDelegate := nil;
FLastMacOsKeyDownCode := 0;
end;
procedure TFMXApplicationService.AfterConstruction;
@ -274,14 +271,6 @@ begin
end;
end;
class function TFMXApplicationService.LastMacOsKeyDownCode: integer;
begin
if assigned(NewFMXApplicationService) then
Result := TFMXApplicationService(NewFMXApplicationService).FLastMacOsKeyDownCode
else
Result := 0;
end;
function TFMXApplicationService.GetDefaultTitle: string;
begin
Result := OldFMXApplicationService.GetDefaultTitle;
@ -377,22 +366,8 @@ begin
Result := nil;
end;
function TFMXApplicationService.HandleMessage: Boolean; {
const
WaitTimeout = 0.001;
var
TempEvent : NSEvent;
TempNSApp : NSApplication;
TempTimeout : NSDate; }
begin {
TempNSApp := TNSApplication.Wrap(TNSApplication.OCClass.sharedApplication);
TempTimeout := TNSDate.Wrap(TNSDate.OCClass.dateWithTimeIntervalSinceNow(WaitTimeout));
TempEvent := TempNSApp.nextEventMatchingMask(NSAnyEventMask, TempTimeout, NSDefaultRunLoopMode, False);
if (TempEvent <> nil) and (TempEvent.&type = NSKeyDown) then
FLastMacOsKeyDownCode := TempEvent.keyCode;
}
function TFMXApplicationService.HandleMessage: Boolean;
begin
Result := OldFMXApplicationService.HandleMessage;
end;

View File

@ -103,6 +103,7 @@ object FMXExternalPumpBrowserFrm: TFMXExternalPumpBrowserFrm
OnMouseUp = Panel1MouseUp
OnMouseLeave = Panel1MouseLeave
OnMouseWheel = Panel1MouseWheel
OnKeyUp = Panel1KeyUp
OnKeyDown = Panel1KeyDown
OnDialogKey = Panel1DialogKey
end

View File

@ -42,15 +42,10 @@ unit uFMXExternalPumpBrowser;
interface
uses
{$IFDEF MSWINDOWS}
Winapi.Messages, Winapi.Windows,
{$ENDIF}
System.Types, System.UITypes, System.Classes, System.SyncObjs,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs,
Macapi.AppKit, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs,
FMX.Edit, FMX.StdCtrls, FMX.Controls.Presentation,
{$IFDEF DELPHI17_UP}
FMX.Graphics,
{$ENDIF}
{$IFDEF DELPHI17_UP}FMX.Graphics,{$ENDIF}
uCEFFMXChromium, uCEFFMXBufferPanel, uCEFFMXWorkScheduler,
uCEFInterfaces, uCEFTypes, uCEFConstants, uCEFChromiumCore, FMX.Layouts,
FMX.Memo.Types, FMX.ScrollBox, FMX.Memo;
@ -87,6 +82,7 @@ type
procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
procedure Panel1MouseLeave(Sender: TObject);
procedure Panel1MouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean);
procedure Panel1KeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
procedure Panel1KeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
procedure Panel1DialogKey(Sender: TObject; var Key: Word; Shift: TShiftState);
@ -139,27 +135,12 @@ type
function GetMousePosition(var aPoint : TPointF) : boolean;
procedure InitializeLastClick;
function CancelPreviousClick(const x, y : single; var aCurrentTime : integer) : boolean;
{$IFDEF MSWINDOWS}
function SendCompMessage(aMsg : cardinal; aWParam : WPARAM = 0; aLParam : LPARAM = 0) : boolean;
function ArePointerEventsSupported : boolean;
function HandlePenEvent(const aID : uint32; aMsg : cardinal) : boolean;
function HandleTouchEvent(const aID : uint32; aMsg : cardinal) : boolean; overload;
function HandlePointerEvent(const aMessage : TMsg) : boolean;
{$ENDIF}
public
procedure DoResize;
procedure NotifyMoveOrResizeStarted;
procedure SendCaptureLostEvent;
procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; AHeight: Integer); override;
{$IFDEF MSWINDOWS}
procedure HandleSYSCHAR(const aMessage : TMsg);
procedure HandleSYSKEYDOWN(const aMessage : TMsg);
procedure HandleSYSKEYUP(const aMessage : TMsg);
procedure HandleKEYDOWN(const aMessage : TMsg);
procedure HandleKEYUP(const aMessage : TMsg);
function HandlePOINTER(const aMessage : TMsg) : boolean;
{$ENDIF}
end;
var
@ -171,7 +152,7 @@ var
// This demo is in ALPHA state. It's incomplete and some features may not work!
// ****************************************************************************
// Known issues and missing features :
// - Keyboard support not implemented yet.
// - Keyboard support is incomplete.
// - Full screen event is not handled correctly.
// - The CrAppProtocol implementation in uFMXApplicationService needs to be tested.
// - All Windows code in this demo must be removed.
@ -393,33 +374,74 @@ begin
chrmosr.SendFocusEvent(False);
end;
procedure TFMXExternalPumpBrowserFrm.Panel1KeyUp(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
var
TempKeyEvent : TCefKeyEvent;
TempChar : char;
begin
if not(Panel1.IsFocused) then exit;
if (KeyChar <> #0) then
TempChar := KeyChar
else
if (Key <> 0) then
TempChar := chr(Key)
else
TempChar := #0;
TempKeyEvent.kind := KEYEVENT_KEYUP;
TempKeyEvent.modifiers := getModifiers(Shift);
TempKeyEvent.windows_key_code := 0;
TempKeyEvent.native_key_code := 0;
TempKeyEvent.is_system_key := ord(False);
TempKeyEvent.character := TempChar;
TempKeyEvent.unmodified_character := TempChar;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
procedure TFMXExternalPumpBrowserFrm.Panel1KeyDown( Sender : TObject;
var Key : Word;
var KeyChar : Char;
Shift : TShiftState);
var
TempKeyEvent : TCefKeyEvent;
TempChar : char;
begin
if not(Panel1.IsFocused) then exit;
if (Key = 0) and (KeyChar <> #0) then
begin
TempKeyEvent.kind := KEYEVENT_CHAR;
TempKeyEvent.modifiers := getModifiers(Shift);
TempKeyEvent.windows_key_code := ord(KeyChar);
// TempKeyEvent.native_key_code := TFMXApplicationService.LastMacOsKeyDownCode;
TempKeyEvent.native_key_code := 0;
TempKeyEvent.is_system_key := ord(False);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end
if (KeyChar <> #0) then
TempChar := KeyChar
else
if (Key <> 0) and (KeyChar = #0) and
(Key in [vkLeft, vkRight, vkUp, vkDown]) then
Key := 0;
if (Key <> 0) then
begin
TempChar := chr(Key);
if (Key in [vkLeft, vkRight, vkUp, vkDown]) then
begin
Key := 0;
exit;
end;
end
else
TempChar := #0;
TempKeyEvent.kind := KEYEVENT_KEYDOWN;
TempKeyEvent.modifiers := getModifiers(Shift);
TempKeyEvent.windows_key_code := 0;
TempKeyEvent.native_key_code := 0;
TempKeyEvent.is_system_key := ord(False);
TempKeyEvent.character := TempChar;
TempKeyEvent.unmodified_character := TempChar;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
TempKeyEvent.kind := KEYEVENT_CHAR;
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
procedure TFMXExternalPumpBrowserFrm.Panel1MouseDown(Sender : TObject;
@ -578,14 +600,10 @@ procedure TFMXExternalPumpBrowserFrm.chrmosrBeforeClose(Sender: TObject; const b
begin
FCanClose := True;
{$IFDEF MSWINDOWS}
SendCompMessage(WM_CLOSE);
{$ELSE}
TThread.Queue(nil, procedure
begin
close
end);
{$ENDIF}
end;
procedure TFMXExternalPumpBrowserFrm.chrmosrBeforePopup( Sender : TObject;
@ -824,13 +842,7 @@ begin
if (type_ = PET_VIEW) then
begin
if TempForcedResize or FPendingResize then
begin
{$IFDEF MSWINDOWS}
SendCompMessage(CEF_PENDINGRESIZE);
{$ELSE}
TThread.Queue(nil, DoResize);
{$ENDIF}
end;
TThread.Queue(nil, DoResize);
FResizing := False;
FPendingResize := False;
@ -981,263 +993,4 @@ begin
chrmosr.SendFocusEvent(False);
end;
{$IFDEF MSWINDOWS}
procedure TFMXExternalPumpBrowserFrm.HandleSYSCHAR(const aMessage : TMsg);
var
TempKeyEvent : TCefKeyEvent;
begin
if Panel1.IsFocused and (aMessage.wParam in [VK_BACK..VK_HELP]) then
begin
TempKeyEvent.kind := KEYEVENT_CHAR;
TempKeyEvent.modifiers := GetCefKeyboardModifiers(aMessage.wParam, aMessage.lParam);
TempKeyEvent.windows_key_code := integer(aMessage.wParam);
TempKeyEvent.native_key_code := integer(aMessage.lParam);
TempKeyEvent.is_system_key := ord(True);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
end;
procedure TFMXExternalPumpBrowserFrm.HandleSYSKEYDOWN(const aMessage : TMsg);
var
TempKeyEvent : TCefKeyEvent;
begin
if Panel1.IsFocused and (aMessage.wParam in [VK_BACK..VK_HELP]) then
begin
TempKeyEvent.kind := KEYEVENT_RAWKEYDOWN;
TempKeyEvent.modifiers := GetCefKeyboardModifiers(aMessage.wParam, aMessage.lParam);
TempKeyEvent.windows_key_code := integer(aMessage.wParam);
TempKeyEvent.native_key_code := integer(aMessage.lParam);
TempKeyEvent.is_system_key := ord(True);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
end;
procedure TFMXExternalPumpBrowserFrm.HandleSYSKEYUP(const aMessage : TMsg);
var
TempKeyEvent : TCefKeyEvent;
begin
if Panel1.IsFocused and (aMessage.wParam in [VK_BACK..VK_HELP]) then
begin
TempKeyEvent.kind := KEYEVENT_KEYUP;
TempKeyEvent.modifiers := GetCefKeyboardModifiers(aMessage.wParam, aMessage.lParam);
TempKeyEvent.windows_key_code := integer(aMessage.wParam);
TempKeyEvent.native_key_code := integer(aMessage.lParam);
TempKeyEvent.is_system_key := ord(True);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
end;
procedure TFMXExternalPumpBrowserFrm.HandleKEYDOWN(const aMessage : TMsg);
var
TempKeyEvent : TCefKeyEvent;
begin
if Panel1.IsFocused then
begin
TempKeyEvent.kind := KEYEVENT_RAWKEYDOWN;
TempKeyEvent.modifiers := GetCefKeyboardModifiers(aMessage.wParam, aMessage.lParam);
TempKeyEvent.windows_key_code := integer(aMessage.wParam);
TempKeyEvent.native_key_code := integer(aMessage.lParam);
TempKeyEvent.is_system_key := ord(False);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
end;
procedure TFMXExternalPumpBrowserFrm.HandleKEYUP(const aMessage : TMsg);
var
TempKeyEvent : TCefKeyEvent;
begin
if Panel1.IsFocused then
begin
if (aMessage.wParam = vkReturn) then
begin
TempKeyEvent.kind := KEYEVENT_CHAR;
TempKeyEvent.modifiers := GetCefKeyboardModifiers(aMessage.wParam, aMessage.lParam);
TempKeyEvent.windows_key_code := integer(aMessage.wParam);
TempKeyEvent.native_key_code := integer(aMessage.lParam);
TempKeyEvent.is_system_key := ord(False);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
TempKeyEvent.kind := KEYEVENT_KEYUP;
TempKeyEvent.modifiers := GetCefKeyboardModifiers(aMessage.wParam, aMessage.lParam);
TempKeyEvent.windows_key_code := integer(aMessage.wParam);
TempKeyEvent.native_key_code := integer(aMessage.lParam);
TempKeyEvent.is_system_key := ord(False);
TempKeyEvent.character := #0;
TempKeyEvent.unmodified_character := #0;
TempKeyEvent.focus_on_editable_field := ord(False);
chrmosr.SendKeyEvent(@TempKeyEvent);
end;
end;
function TFMXExternalPumpBrowserFrm.HandlePOINTER(const aMessage : TMsg) : boolean;
begin
Result := Panel1.IsFocused and
(GlobalCEFApp <> nil) and
ArePointerEventsSupported and
HandlePointerEvent(aMessage);
end;
function TFMXExternalPumpBrowserFrm.SendCompMessage(aMsg : cardinal; aWParam : WPARAM; aLParam : LPARAM) : boolean;
var
TempHandle : TWinWindowHandle;
begin
TempHandle := WindowHandleToPlatform(Handle);
Result := WinApi.Windows.PostMessage(TempHandle.Wnd, aMsg, aWParam, aLParam);
end;
function TFMXExternalPumpBrowserFrm.ArePointerEventsSupported : boolean;
begin
Result := FAtLeastWin8 and
(@GetPointerType <> nil) and
(@GetPointerTouchInfo <> nil) and
(@GetPointerPenInfo <> nil);
end;
function TFMXExternalPumpBrowserFrm.HandlePointerEvent(const aMessage : TMsg) : boolean;
const
PT_TOUCH = 2;
PT_PEN = 3;
var
TempID : uint32;
TempType : POINTER_INPUT_TYPE;
begin
Result := False;
TempID := LoWord(aMessage.wParam);
if GetPointerType(TempID, @TempType) then
case TempType of
PT_PEN : Result := HandlePenEvent(TempID, aMessage.message);
PT_TOUCH : Result := HandleTouchEvent(TempID, aMessage.message);
end;
end;
function TFMXExternalPumpBrowserFrm.HandlePenEvent(const aID : uint32; aMsg : cardinal) : boolean;
var
TempPenInfo : POINTER_PEN_INFO;
TempTouchEvent : TCefTouchEvent;
TempPoint : TPoint;
begin
Result := False;
if not(GetPointerPenInfo(aID, @TempPenInfo)) then exit;
TempTouchEvent.id := aID;
TempTouchEvent.x := 0;
TempTouchEvent.y := 0;
TempTouchEvent.radius_x := 0;
TempTouchEvent.radius_y := 0;
TempTouchEvent.type_ := CEF_TET_RELEASED;
TempTouchEvent.modifiers := EVENTFLAG_NONE;
if ((TempPenInfo.penFlags and PEN_FLAG_ERASER) <> 0) then
TempTouchEvent.pointer_type := CEF_POINTER_TYPE_ERASER
else
TempTouchEvent.pointer_type := CEF_POINTER_TYPE_PEN;
if ((TempPenInfo.penMask and PEN_MASK_PRESSURE) <> 0) then
TempTouchEvent.pressure := TempPenInfo.pressure / 1024
else
TempTouchEvent.pressure := 0;
if ((TempPenInfo.penMask and PEN_MASK_ROTATION) <> 0) then
TempTouchEvent.rotation_angle := TempPenInfo.rotation / 180 * Pi
else
TempTouchEvent.rotation_angle := 0;
Result := True;
case aMsg of
WM_POINTERDOWN :
TempTouchEvent.type_ := CEF_TET_PRESSED;
WM_POINTERUPDATE :
if ((TempPenInfo.pointerInfo.pointerFlags and POINTER_FLAG_INCONTACT) <> 0) then
TempTouchEvent.type_ := CEF_TET_MOVED
else
exit; // Ignore hover events.
WM_POINTERUP :
TempTouchEvent.type_ := CEF_TET_RELEASED;
end;
if ((TempPenInfo.pointerInfo.pointerFlags and POINTER_FLAG_CANCELED) <> 0) then
TempTouchEvent.type_ := CEF_TET_CANCELLED;
TempPoint := Panel1.ScreenToClient(TempPenInfo.pointerInfo.ptPixelLocation);
// TFMXBufferPanel.ScreenToClient applies the scale factor. No need to call DeviceToLogical to set TempTouchEvent.
TempTouchEvent.x := TempPoint.x;
TempTouchEvent.y := TempPoint.y;
chrmosr.SendTouchEvent(@TempTouchEvent);
end;
function TFMXExternalPumpBrowserFrm.HandleTouchEvent(const aID : uint32; aMsg : cardinal) : boolean;
var
TempTouchInfo : POINTER_TOUCH_INFO;
TempTouchEvent : TCefTouchEvent;
TempPoint : TPoint;
begin
Result := False;
if not(GetPointerTouchInfo(aID, @TempTouchInfo)) then exit;
TempTouchEvent.id := aID;
TempTouchEvent.x := 0;
TempTouchEvent.y := 0;
TempTouchEvent.radius_x := 0;
TempTouchEvent.radius_y := 0;
TempTouchEvent.rotation_angle := 0;
TempTouchEvent.pressure := 0;
TempTouchEvent.type_ := CEF_TET_RELEASED;
TempTouchEvent.modifiers := EVENTFLAG_NONE;
TempTouchEvent.pointer_type := CEF_POINTER_TYPE_TOUCH;
Result := True;
case aMsg of
WM_POINTERDOWN :
TempTouchEvent.type_ := CEF_TET_PRESSED;
WM_POINTERUPDATE :
if ((TempTouchInfo.pointerInfo.pointerFlags and POINTER_FLAG_INCONTACT) <> 0) then
TempTouchEvent.type_ := CEF_TET_MOVED
else
exit; // Ignore hover events.
WM_POINTERUP :
TempTouchEvent.type_ := CEF_TET_RELEASED;
end;
if ((TempTouchInfo.pointerInfo.pointerFlags and POINTER_FLAG_CANCELED) <> 0) then
TempTouchEvent.type_ := CEF_TET_CANCELLED;
TempPoint := Panel1.ScreenToClient(TempTouchInfo.pointerInfo.ptPixelLocation);
// TFMXBufferPanel.ScreenToClient applies the scale factor. No need to call DeviceToLogical to set TempTouchEvent.
TempTouchEvent.x := TempPoint.x;
TempTouchEvent.y := TempPoint.y;
chrmosr.SendTouchEvent(@TempTouchEvent);
end;
{$ENDIF}
end.