1
0
mirror of https://github.com/salvadordf/CEF4Delphi.git synced 2025-09-30 21:28:55 +02:00

Added SimpleOSRBrowser demo for Lazarus using GTK3

Added uCEFLinuxOSRIMEHandler to the CEF4Delphi source directory.
Updated SimpleOSRBrowser for GTK2
This commit is contained in:
Salvador Díaz Fau
2025-08-25 19:17:25 +02:00
parent 6fc5983d2f
commit 372bea8377
17 changed files with 2985 additions and 850 deletions

View File

@@ -37,7 +37,7 @@
<PackageName Value="LCL"/>
</Item3>
</RequiredPackages>
<Units Count="4">
<Units Count="3">
<Unit0>
<Filename Value="SimpleOSRBrowser.lpr"/>
<IsPartOfProject Value="True"/>
@@ -55,11 +55,6 @@
<IsPartOfProject Value="True"/>
<UnitName Value="Interfaces"/>
</Unit2>
<Unit3>
<Filename Value="uceflinuxosrimehandler.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uCEFLinuxOSRIMEHandler"/>
</Unit3>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@@ -9,7 +9,7 @@ uses
// "Interfaces" is a custom unit used to initialize the LCL WidgetSet
// We keep the same name to avoid a Lazarus warning.
Interfaces, // this includes the LCL widgetset
Forms, lazmouseandkeyinput, uSimpleOSRBrowser, uCEFLinuxOSRIMEHandler,
Forms, lazmouseandkeyinput, uSimpleOSRBrowser,
{ you can add units after this }
uCEFApplication;

View File

@@ -1,11 +1,13 @@
object Form1: TForm1
Left = 1658
Left = 561
Height = 630
Top = 423
Top = 252
Width = 1000
Caption = ' Initializing browser. Please wait...'
ClientHeight = 630
ClientWidth = 1000
Position = poScreenCenter
LCLVersion = '4.2.0.0'
OnActivate = FormActivate
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
@@ -13,7 +15,6 @@ object Form1: TForm1
OnHide = FormHide
OnShow = FormShow
OnWindowStateChange = FormWindowStateChange
Position = poScreenCenter
object AddressPnl: TPanel
Left = 0
Height = 30
@@ -66,7 +67,7 @@ object Form1: TForm1
Align = alClient
AutoSelect = False
ItemHeight = 0
ItemIndex = 7
ItemIndex = 0
Items.Strings = (
'https://www.google.com'
'https://www.bing.com'
@@ -125,7 +126,7 @@ object Form1: TForm1
'chrome://process-internals'
)
TabOrder = 0
Text = 'https://www.baidu.com'
Text = 'https://www.google.com'
OnEnter = AddressCbEnter
end
end
@@ -138,6 +139,8 @@ object Form1: TForm1
OnIMEPreEditEnd = Panel1IMEPreEditEnd
OnIMEPreEditChanged = Panel1IMEPreEditChanged
OnIMECommit = Panel1IMECommit
OnGdkKeyPress = Panel1GdkKeyPress
OnGdkKeyRelease = Panel1GdkKeyRelease
Align = alClient
Caption = 'Panel1'
Color = clWhite

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
rmdir /S /Q lib
rmdir /S /Q backup

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<CompatibilityMode Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="SimpleOSRBrowser"/>
<Scaled Value="True"/>
<ResourceType Value="res"/>
<UseXPManifest Value="True"/>
<XPManifest>
<DpiAware Value="True"/>
</XPManifest>
<Icon Value="0"/>
</General>
<MacroValues Count="1">
<Macro1 Name="LCLWidgetType" Value="gtk3"/>
</MacroValues>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
<SharedMatrixOptions Count="1">
<Item1 ID="996268604706" Modes="Default" Type="IDEMacro" MacroName="LCLWidgetType" Value="gtk3"/>
</SharedMatrixOptions>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
</RunParams>
<RequiredPackages Count="3">
<Item1>
<PackageName Value="lazmouseandkeyinput"/>
</Item1>
<Item2>
<PackageName Value="CEF4Delphi_Lazarus"/>
</Item2>
<Item3>
<PackageName Value="LCL"/>
</Item3>
</RequiredPackages>
<Units Count="3">
<Unit0>
<Filename Value="SimpleOSRBrowser.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
<Unit1>
<Filename Value="usimpleosrbrowser.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="MainForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="uSimpleOSRBrowser"/>
</Unit1>
<Unit2>
<Filename Value="interfaces.pas"/>
<IsPartOfProject Value="True"/>
</Unit2>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
<Filename Value="../../../bin/SimpleOSRBrowser"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf3"/>
</Debugging>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
<Other>
<CustomOptions Value="-dUseCthreads"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,36 @@
program SimpleOSRBrowser;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
// "Interfaces" is a custom unit used to initialize the LCL WidgetSet
// We keep the same name to avoid a Lazarus warning.
Interfaces, // this includes the LCL widgetset
Forms, lazmouseandkeyinput, uSimpleOSRBrowser,
{ you can add units after this }
uCEFApplication;
{$R *.res}
begin
CreateGlobalCEFApp;
if GlobalCEFApp.StartMainProcess then
begin
// The LCL Widgetset must be initialized after the CEF initialization and
// only in the browser process.
CustomWidgetSetInitialization;
RequireDerivedFormResource:=True;
Application.Scaled:=True;
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
CustomWidgetSetFinalization;
end;
DestroyGlobalCEFApp;
end.

View File

@@ -0,0 +1,67 @@
{
/***************************************************************************
Interfaces.pp - determines what interface to use
-------------------
Initial Revision : Thu July 1st CST 1999
***************************************************************************/
*****************************************************************************
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 interfaces;
{$mode objfpc}
{$H+}
interface
uses
{$IFDEF UNIX}{$IFNDEF DisableCWString}cwstring,{$ENDIF}{$ENDIF}
InterfaceBase;
procedure CustomWidgetSetInitialization;
procedure CustomWidgetSetFinalization;
implementation
uses
gtk3int, Forms, xlib;
function CustomX11ErrorHandler(Display:PDisplay; ErrorEv:PXErrorEvent):longint;cdecl;
begin
{$IFDEF DEBUG}
XError := ErrorEv^.error_code;
WriteLn('Error: ' + IntToStr(XError));
{$ENDIF}
Result := 0;
end;
function CustomXIOErrorHandler(Display:PDisplay):longint;cdecl;
begin
Result := 0;
end;
procedure CustomWidgetSetInitialization;
begin
//gdk_set_allowed_backends('X11');
CreateWidgetset(TGtk3WidgetSet);
// Install xlib error handlers so that the application won't be terminated
// on non-fatal errors. Must be done after initializing GTK.
XSetErrorHandler(@CustomX11ErrorHandler);
XSetIOErrorHandler(@CustomXIOErrorHandler);
end;
procedure CustomWidgetSetFinalization;
begin
FreeWidgetSet;
end;
end.

View File

@@ -6,6 +6,7 @@ interface
uses
{$IFDEF LCLGTK2}gtk2, glib2, gdk2,{$ENDIF}
{$IFDEF LCLGTK3}LazGdk3, LazGtk3, LazGObject2, LazGLib2, gtk3procs, gtk3widgets,{$ENDIF}
Classes, ExtCtrls, Forms;
type
@@ -14,9 +15,9 @@ type
FPanel : TCustomPanel;
FForm : TCustomForm;
FHasFocus : boolean;
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
FIMContext : PGtkIMContext;
{$ENDIF}
{$IFEND}
function GetInitialized : boolean;
procedure SetPanel(aValue : TCustomPanel);
@@ -33,9 +34,9 @@ type
procedure Blur;
procedure Reset;
procedure SetCursorLocation(X, Y: integer);
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function FilterKeyPress(aEvent : PGdkEventKey) : boolean;
{$ENDIF}
{$ENDIF}
property Initialized : boolean read GetInitialized;
property HasFocus : boolean read FHasFocus;
@@ -48,11 +49,11 @@ implementation
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ui/gtk/input_method_context_impl_gtk.cc
uses
{$IFDEF LCLGTK2}pango,{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}pango,{$ENDIF}
{$IFDEF FPC}LCLType, LCLIntf, LMessages,{$ENDIF}
SysUtils;
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
procedure gtk_commit_cb({%H-}context: PGtkIMContext; const Str: Pgchar; {%H-}Data: Pointer); cdecl;
begin
SendMessage(HWND(Data), LM_IM_COMPOSITION, GTK_IM_FLAG_COMMIT, LPARAM(Str));
@@ -74,7 +75,7 @@ var
TempPangoAttr : PPangoAttrList;
TempCurpos : gint;
begin
gtk_im_context_get_preedit_string(context, @TempStr, TempPangoAttr, @TempCurpos);
gtk_im_context_get_preedit_string(context, @TempStr, {$IFDEF LCLGTK3}@{$ENDIF}TempPangoAttr, @TempCurpos);
SendMessage(HWND(Data), LM_IM_COMPOSITION, GTK_IM_FLAG_PREEDIT, LPARAM(pchar(TempStr)));
g_free(TempStr);
pango_attr_list_unref(TempPangoAttr);
@@ -87,7 +88,7 @@ begin
FPanel := aPanel;
FHasFocus := False;
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
FIMContext := nil;
{$ENDIF}
@@ -107,7 +108,7 @@ end;
function TCEFLinuxOSRIMEHandler.GetInitialized : boolean;
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
Result := assigned(FPanel) and assigned(FIMContext);
{$ELSE}
Result := False;
@@ -127,7 +128,7 @@ end;
procedure TCEFLinuxOSRIMEHandler.CreateContext;
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
if not(assigned(FIMContext)) then
begin
FIMContext := gtk_im_multicontext_new();
@@ -139,7 +140,7 @@ end;
procedure TCEFLinuxOSRIMEHandler.DestroyContext;
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
if assigned(FIMContext) then
begin
g_object_unref(FIMContext);
@@ -149,7 +150,7 @@ begin
end;
procedure TCEFLinuxOSRIMEHandler.SetClientWindow;
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
var
TempWidget : PGtkWidget;
{$ENDIF}
@@ -160,6 +161,10 @@ begin
TempWidget := PGtkWidget(FForm.Handle);
gtk_im_context_set_client_window(FIMContext, TempWidget^.window);
{$ENDIF}
{$IFDEF LCLGTK3}
TempWidget := TGtk3Widget(FForm.Handle).Widget;
gtk_im_context_set_client_window(FIMContext, TempWidget^.window);
{$ENDIF}
end;
end;
@@ -167,7 +172,7 @@ procedure TCEFLinuxOSRIMEHandler.ResetClientWindow;
begin
if Initialized then
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_reset(FIMContext);
gtk_im_context_set_client_window(FIMContext, nil);
{$ENDIF}
@@ -178,6 +183,12 @@ procedure TCEFLinuxOSRIMEHandler.ConnectSignals;
begin
if Initialized then
begin
{$IFDEF LCLGTK3}
g_signal_connect_data(PGObject(@FIMContext), 'commit', TGCallback(@gtk_commit_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
g_signal_connect_data(PGObject(@FIMContext), 'preedit-start', TGCallback(@gtk_preedit_start_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
g_signal_connect_data(PGObject(@FIMContext), 'preedit-end', TGCallback(@gtk_preedit_end_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
g_signal_connect_data(PGObject(@FIMContext), 'preedit-changed', TGCallback(@gtk_preedit_changed_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
{$ENDIF}
{$IFDEF LCLGTK2}
g_signal_connect(G_OBJECT(FIMContext), 'commit', G_CALLBACK(@gtk_commit_cb), GPointer(FPanel.Handle));
g_signal_connect(G_OBJECT(FIMContext), 'preedit-start', G_CALLBACK(@gtk_preedit_start_cb), GPointer(FPanel.Handle));
@@ -191,7 +202,7 @@ procedure TCEFLinuxOSRIMEHandler.Focus;
begin
if Initialized then
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_focus_in(FIMContext);
{$ENDIF}
FHasFocus := True;
@@ -202,7 +213,7 @@ procedure TCEFLinuxOSRIMEHandler.Blur;
begin
if Initialized then
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_focus_out(FIMContext);
{$ENDIF}
FHasFocus := False;
@@ -213,7 +224,7 @@ procedure TCEFLinuxOSRIMEHandler.Reset;
begin
if Initialized then
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_reset(FIMContext);
// Some input methods may not honour the reset call.
@@ -228,14 +239,14 @@ begin
end;
procedure TCEFLinuxOSRIMEHandler.SetCursorLocation(X, Y: integer);
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
var
TempCurPos: TGdkRectangle;
{$ENDIF}
begin
if Initialized then
begin
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
TempCurPos.x := x;
TempCurPos.y := y;
TempCurPos.width := 0;
@@ -246,7 +257,7 @@ begin
end;
end;
{$IFDEF LCLGTK2}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function TCEFLinuxOSRIMEHandler.FilterKeyPress(aEvent : PGdkEventKey) : boolean;
begin
if Initialized then

View File

@@ -0,0 +1,162 @@
object MainForm: TMainForm
Left = 1658
Height = 630
Top = 423
Width = 1000
Caption = ' Initializing browser. Please wait...'
ClientHeight = 630
ClientWidth = 1000
Position = poScreenCenter
LCLVersion = '4.2.0.0'
OnActivate = FormActivate
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
OnDestroy = FormDestroy
OnHide = FormHide
OnShow = FormShow
OnWindowStateChange = FormWindowStateChange
object AddressPnl: TPanel
Left = 0
Height = 30
Top = 0
Width = 1000
Align = alTop
ClientHeight = 30
ClientWidth = 1000
TabOrder = 0
object AddressCb: TComboBox
Left = 1
Height = 28
Top = 1
Width = 967
Align = alClient
AutoSelect = False
ItemHeight = 0
ItemIndex = 0
Items.Strings = (
'https://www.google.com'
'https://www.bing.com'
'https://duckduckgo.com'
'https://www.qwant.com'
'https://yandex.com'
'https://www.startpage.com'
'https://www.ecosia.org'
'https://www.baidu.com'
'https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending'
'https://www.w3schools.com/js/tryit.asp?filename=tryjs_win_close'
'https://www.w3schools.com/js/tryit.asp?filename=tryjs_alert'
'https://www.w3schools.com/js/tryit.asp?filename=tryjs_loc_assign'
'https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_style_backgroundcolor'
'https://www.w3schools.com/Tags/tryit.asp?filename=tryhtml_iframe_name'
'https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file'
'https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_state_throw_error'
'https://www.htmlquick.com/es/reference/tags/input-file.html'
'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file'
'https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory'
'https://www.w3schools.com/html/html5_video.asp'
'http://html5test.com/'
'https://webrtc.github.io/samples/src/content/devices/input-output/'
'https://test.webrtc.org/'
'https://www.browserleaks.com/webrtc'
'https://shaka-player-demo.appspot.com/demo/'
'http://webglsamples.org/'
'https://get.webgl.org/'
'https://www.briskbard.com'
'https://www.youtube.com'
'https://html5demos.com/drag/'
'https://frames-per-second.appspot.com/'
'https://www.sede.fnmt.gob.es/certificados/persona-fisica/verificar-estado'
'https://www.kirupa.com/html5/accessing_your_webcam_in_html5.htm'
'https://www.xdumaine.com/enumerateDevices/test/'
'https://dagrs.berkeley.edu/sites/default/files/2020-01/sample.pdf'
'https://codepen.io/udaymanvar/pen/MWaePBY'
'https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept'
'chrome://version/'
'chrome://net-internals/'
'chrome://tracing/'
'chrome://appcache-internals/'
'chrome://blob-internals/'
'chrome://view-http-cache/'
'chrome://credits/'
'chrome://histograms/'
'chrome://media-internals/'
'chrome://kill'
'chrome://crash'
'chrome://hang'
'chrome://shorthang'
'chrome://gpuclean'
'chrome://gpucrash'
'chrome://gpuhang'
'chrome://extensions-support'
'chrome://process-internals'
)
TabOrder = 0
Text = 'https://www.google.com'
OnEnter = AddressCbEnter
end
object GoBtn: TButton
Left = 968
Height = 28
Top = 1
Width = 31
Align = alRight
Caption = 'Go'
TabOrder = 1
OnClick = GoBtnClick
OnEnter = GoBtnEnter
end
end
object Panel1: TBufferPanel
Tag = 99
Left = 0
Height = 600
Top = 30
Width = 1000
OnIMEPreEditStart = Panel1IMEPreEditStart
OnIMEPreEditEnd = Panel1IMEPreEditEnd
OnIMEPreEditChanged = Panel1IMEPreEditChanged
OnIMECommit = Panel1IMECommit
OnGdkKeyPress = Panel1GdkKeyPress
OnGdkKeyRelease = Panel1GdkKeyRelease
CopyOriginalBuffer = True
Align = alClient
Color = clWhite
ParentColor = False
TabOrder = 1
TabStop = True
OnEnter = Panel1Enter
OnExit = Panel1Exit
OnMouseDown = Panel1MouseDown
OnMouseMove = Panel1MouseMove
OnMouseUp = Panel1MouseUp
OnMouseWheel = Panel1MouseWheel
OnResize = Panel1Resize
OnMouseEnter = Panel1MouseEnter
OnMouseLeave = Panel1MouseLeave
end
object Chromium1: TChromium
OnProcessMessageReceived = Chromium1ProcessMessageReceived
OnSetFocus = Chromium1SetFocus
OnTooltip = Chromium1Tooltip
OnCursorChange = Chromium1CursorChange
OnBeforePopup = Chromium1BeforePopup
OnAfterCreated = Chromium1AfterCreated
OnBeforeClose = Chromium1BeforeClose
OnOpenUrlFromTab = Chromium1OpenUrlFromTab
OnGetViewRect = Chromium1GetViewRect
OnGetScreenPoint = Chromium1GetScreenPoint
OnGetScreenInfo = Chromium1GetScreenInfo
OnPopupShow = Chromium1PopupShow
OnPopupSize = Chromium1PopupSize
OnPaint = Chromium1Paint
OnDevToolsMethodResult = Chromium1DevToolsMethodResult
Left = 48
Top = 72
end
object Timer1: TTimer
Enabled = False
OnTimer = Timer1Timer
Left = 48
Top = 152
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -901,6 +901,10 @@
<Filename Value="..\source\uCEFSettingObserver.pas"/>
<UnitName Value="uCEFSettingObserver"/>
</Item215>
<Item216>
<Filename Value="..\source\uceflinuxosrimehandler.pas"/>
<UnitName Value="uCEFLinuxOSRIMEHandler"/>
</Item216>
</Files>
<CompatibilityMode Value="True"/>
<RequiredPkgs Count="5">

View File

@@ -15,10 +15,12 @@ uses
{$ELSE}
{$IFDEF MSWINDOWS}Windows, imm, {$ENDIF} Classes, Forms, Controls, Graphics,
{$IFDEF FPC}
LCLProc, LCLType, LCLIntf, LResources, LMessages, InterfaceBase, {$IFDEF MSWINDOWS}Win32Extra,{$ENDIF}
{$IFDEF LINUXFPC}Messages,{$ENDIF}
LCLProc, LCLType, LCLIntf, LResources, LMessages, InterfaceBase, {$IFDEF MSWINDOWS}Win32Extra,{$ENDIF}
{$IFDEF LINUXFPC}Messages,{$ENDIF}
{$IFDEF LCLGTK2}glib2, gdk2, gtk2,{$ENDIF}
{$IFDEF LCLGTK3}LazGdk3, LazGtk3, LazGObject2, LazGLib2, gtk3procs, gtk3objects, gtk3widgets,{$ENDIF}
{$ELSE}
Messages,
Messages,
{$ENDIF}
ExtCtrls, SyncObjs, SysUtils,
{$ENDIF}
@@ -31,6 +33,9 @@ type
TOnIMEPreEditChangedEvent = procedure(Sender: TObject; aFlag: cardinal; const aPreEditText: ustring) of object;
TOnIMECommitEvent = procedure(Sender: TObject; const aCommitText: ustring) of object;
{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
TOnGdkKeyEvent = procedure(Sender: TObject; aEvent: PGdkEventKey; var aHandled: boolean) of object;
{$ENDIF}
{$IFDEF MSWINDOWS}
TOnHandledMessageEvent = procedure(Sender: TObject; var aMessage: TMessage; var aHandled : boolean) of object;
{$ENDIF}
@@ -72,6 +77,10 @@ type
FOnIMEPreEditChanged : TOnIMEPreEditChangedEvent;
FOnIMECommit : TOnIMECommitEvent;
{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
FOnGdkKeyPress : TOnGdkKeyEvent;
FOnGdkKeyRelease : TOnGdkKeyEvent;
{$ENDIF}
procedure CreateSyncObj;
@@ -118,7 +127,11 @@ type
procedure DoOnIMESetComposition(const aText : ustring; const underlines : TCefCompositionUnderlineDynArray; const replacement_range, selection_range : TCefRange); virtual;
{$ENDIF}
{$IFDEF LINUXFPC}
procedure WMIMEComposition(var aMessage: TMessage); message LM_IM_COMPOSITION;
procedure WMIMEComposition(var aMessage : TMessage); message LM_IM_COMPOSITION;
{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function DoOnGdkKeyPress(aEvent : PGdkEventKey) : boolean; virtual;
function DoOnGdkKeyRelease(aEvent : PGdkEventKey) : boolean; virtual;
{$ENDIF}
public
@@ -187,6 +200,14 @@ type
/// Copy the contents from the original popup buffer copy to the main buffer copy.
/// </summary>
procedure DrawOrigPopupBuffer(const aSrcRect, aDstRect : TRect);
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
/// <summary>
/// Connects the GTK signals used to receive key press events.
/// </summary>
function ConnectSignals: boolean;
{$ENDIF}
/// <summary>
/// Returns the scanline size.
/// </summary>
@@ -379,6 +400,24 @@ type
/// </remarks>
property OnIMECommit : TOnIMECommitEvent read FOnIMECommit write FOnIMECommit;
{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
/// <summary>
/// Event triggered when the key-press-event signal is received.
/// </summary>
/// <remarks>
/// <para><see href="https://docs.gtk.org/gtk3/signal.Widget.key-press-event.html">See the key-press-event article.</see></para>
/// <para>This event only works in GTK2 and GTK3 projects.</para>
/// </remarks>
property OnGdkKeyPress : TOnGdkKeyEvent read FOnGdkKeyPress write FOnGdkKeyPress;
/// <summary>
/// Event triggered when the key-release-event signal is received.
/// </summary>
/// <remarks>
/// <para><see href="https://docs.gtk.org/gtk3/signal.Widget.key-release-event.html">See the key-release-event article.</see></para>
/// <para>This event only works in GTK2 and GTK3 projects.</para>
/// </remarks>
property OnGdkKeyRelease : TOnGdkKeyEvent read FOnGdkKeyRelease write FOnGdkKeyRelease;
{$ENDIF}
/// <summary>
/// Event triggered before the AlphaBlend call that transfer the web contents from the
/// bitmap buffer to the panel when the Transparent property is True.
@@ -552,6 +591,11 @@ begin
FOnIMEPreEditChanged := nil;
FOnIMECommit := nil;
{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
FOnGdkKeyPress := nil;
FOnGdkKeyRelease := nil;
ControlStyle := ControlStyle - [csNoFocus];
{$ENDIF}
end;
destructor TBufferPanel.Destroy;
@@ -566,6 +610,24 @@ begin
inherited Destroy;
end;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function GdkKeyPressCallback(Widget: PGtkWidget; Event: PGdkEventKey; Data: gPointer) : GBoolean; cdecl;
begin
if assigned(Data) then
Result := TBufferPanel(Data).DoOnGdkKeyPress(Event)
else
Result := False;
end;
function GdkKeyReleaseCallback(Widget: PGtkWidget; Event: PGdkEventKey; Data: gPointer) : GBoolean; cdecl;
begin
if assigned(Data) then
Result := TBufferPanel(Data).DoOnGdkKeyRelease(Event)
else
Result := False;
end;
{$ENDIF}
procedure TBufferPanel.AfterConstruction;
begin
inherited AfterConstruction;
@@ -581,6 +643,38 @@ begin
{$ENDIF}
end;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function TBufferPanel.ConnectSignals: boolean;
var
TempHandlerID1, TempHandlerID2 : gulong;
begin
TempHandlerID1 := 0;
TempHandlerID2 := 0;
try
try
if assigned(Parent) then
begin
{$IFDEF LCLGTK2}
TempHandlerID1 := g_signal_connect(PGtkWidget(Handle), 'key-press-event', G_CALLBACK(@GdkKeyPressCallback), gpointer(self));
TempHandlerID2 := g_signal_connect(PGtkWidget(Handle), 'key-release-event', G_CALLBACK(@GdkKeyReleaseCallback), gpointer(self));
{$ENDIF}
{$IFDEF LCLGTK3}
gtk_widget_set_can_focus(TGtk3Widget(Handle).Widget, True);
gtk_widget_add_events(TGtk3Widget(Handle).Widget, gint(GDK_KEY_PRESS_MASK));
TempHandlerID1 := g_signal_connect_data(TGtk3Widget(Handle).Widget, 'key-press-event', TGCallback(@GdkKeyPressCallback), gpointer(self), nil, G_CONNECT_DEFAULT);
TempHandlerID2 := g_signal_connect_data(TGtk3Widget(Handle).Widget, 'key-release-event', TGCallback(@GdkKeyReleaseCallback), gpointer(self), nil, G_CONNECT_DEFAULT);
{$ENDIF}
end;
except
on e : exception do
if CustomExceptionHandler('TBufferPanel.ConnectSignals', e) then raise;
end;
finally
Result := (TempHandlerID1 > 0) and (TempHandlerID2 > 0);
end;
end;
{$ENDIF}
procedure TBufferPanel.CreateIMEHandler;
begin
{$IFDEF MSWINDOWS}
@@ -1064,7 +1158,7 @@ end;
{$IFDEF LINUXFPC}
// The LM_IM_COMPOSITION message is only used by Lazarus in GTK2 when WITH_GTK2_IM is defined.
// You need to open IDE dialog "Tools / Configure 'Build Lazarus'", and there enable the define: WITH_GTK2_IM; then recompile the IDE.
procedure TBufferPanel.WMIMEComposition(var aMessage: TMessage);
procedure TBufferPanel.WMIMEComposition(var aMessage : TMessage);
var
TempText : ustring;
TempCommit : string;
@@ -1095,6 +1189,21 @@ begin
end;
end;
{$ENDIF}
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function TBufferPanel.DoOnGdkKeyPress(aEvent : PGdkEventKey) : boolean;
begin
Result := True;
if assigned(FOnGdkKeyPress) then
FOnGdkKeyPress(self, aEvent, Result);
end;
function TBufferPanel.DoOnGdkKeyRelease(aEvent : PGdkEventKey) : boolean;
begin
Result := True;
if assigned(FOnGdkKeyRelease) then
FOnGdkKeyRelease(self, aEvent, Result);
end;
{$ENDIF}
function TBufferPanel.GetBufferBits : pointer;
begin

View File

@@ -0,0 +1,269 @@
unit uCEFLinuxOSRIMEHandler;
{$mode ObjFPC}{$H+}
interface
uses
{$IFDEF LCLGTK2}gtk2, glib2, gdk2,{$ENDIF}
{$IFDEF LCLGTK3}LazGdk3, LazGtk3, LazGObject2, LazGLib2, gtk3procs, gtk3widgets,{$ENDIF}
Classes, ExtCtrls, Forms;
type
TCEFLinuxOSRIMEHandler = class
protected
FPanel : TCustomPanel;
FForm : TCustomForm;
FHasFocus : boolean;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
FIMContext : PGtkIMContext;
{$IFEND}
function GetInitialized : boolean;
procedure SetPanel(aValue : TCustomPanel);
public
constructor Create(aPanel : TCustomPanel);
destructor Destroy; override;
procedure CreateContext;
procedure DestroyContext;
procedure SetClientWindow;
procedure ResetClientWindow;
procedure ConnectSignals;
procedure Focus;
procedure Blur;
procedure Reset;
procedure SetCursorLocation(X, Y: integer);
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function FilterKeyPress(aEvent : PGdkEventKey) : boolean;
{$ENDIF}
property Initialized : boolean read GetInitialized;
property HasFocus : boolean read FHasFocus;
property Panel : TCustomPanel read FPanel write SetPanel;
end;
implementation
// https://chromium.googlesource.com/chromium/src/+/4079d37114e1dd416e99d5edc535f4214b787fc7/chrome/browser/ui/gtk/input_method_context_impl_gtk.cc
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ui/gtk/input_method_context_impl_gtk.cc
uses
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}pango,{$ENDIF}
{$IFDEF FPC}LCLType, LCLIntf, LMessages,{$ENDIF}
SysUtils;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
procedure gtk_commit_cb({%H-}context: PGtkIMContext; const Str: Pgchar; {%H-}Data: Pointer); cdecl;
begin
SendMessage(HWND(Data), LM_IM_COMPOSITION, GTK_IM_FLAG_COMMIT, LPARAM(Str));
end;
procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
begin
SendMessage(HWND(Data), LM_IM_COMPOSITION, GTK_IM_FLAG_START, LPARAM(context));
end;
procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
begin
SendMessage(HWND(Data), LM_IM_COMPOSITION, GTK_IM_FLAG_END, LPARAM(context));
end;
procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
var
TempStr : Pgchar;
TempPangoAttr : PPangoAttrList;
TempCurpos : gint;
begin
gtk_im_context_get_preedit_string(context, @TempStr, {$IFDEF LCLGTK3}@{$ENDIF}TempPangoAttr, @TempCurpos);
SendMessage(HWND(Data), LM_IM_COMPOSITION, GTK_IM_FLAG_PREEDIT, LPARAM(pchar(TempStr)));
g_free(TempStr);
pango_attr_list_unref(TempPangoAttr);
end;
{$ENDIF}
constructor TCEFLinuxOSRIMEHandler.Create(aPanel : TCustomPanel);
begin
inherited Create;
FPanel := aPanel;
FHasFocus := False;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
FIMContext := nil;
{$ENDIF}
if assigned(FPanel) then
FForm := GetParentForm(FPanel)
else
FForm := nil;
end;
destructor TCEFLinuxOSRIMEHandler.Destroy;
begin
ResetClientWindow;
DestroyContext;
inherited Destroy;
end;
function TCEFLinuxOSRIMEHandler.GetInitialized : boolean;
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
Result := assigned(FPanel) and assigned(FIMContext);
{$ELSE}
Result := False;
{$ENDIF}
end;
procedure TCEFLinuxOSRIMEHandler.SetPanel(aValue : TCustomPanel);
begin
FPanel := aValue;
if assigned(FPanel) then
FForm := GetParentForm(FPanel)
else
FForm := nil;
end;
procedure TCEFLinuxOSRIMEHandler.CreateContext;
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
if not(assigned(FIMContext)) then
begin
FIMContext := gtk_im_multicontext_new();
SetClientWindow;
ConnectSignals;
end;
{$ENDIF}
end;
procedure TCEFLinuxOSRIMEHandler.DestroyContext;
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
if assigned(FIMContext) then
begin
g_object_unref(FIMContext);
FIMContext := nil;
end;
{$ENDIF}
end;
procedure TCEFLinuxOSRIMEHandler.SetClientWindow;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
var
TempWidget : PGtkWidget;
{$ENDIF}
begin
if Initialized then
begin
{$IFDEF LCLGTK2}
TempWidget := PGtkWidget(FForm.Handle);
gtk_im_context_set_client_window(FIMContext, TempWidget^.window);
{$ENDIF}
{$IFDEF LCLGTK3}
TempWidget := TGtk3Widget(FForm.Handle).Widget;
gtk_im_context_set_client_window(FIMContext, TempWidget^.window);
{$ENDIF}
end;
end;
procedure TCEFLinuxOSRIMEHandler.ResetClientWindow;
begin
if Initialized then
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_reset(FIMContext);
gtk_im_context_set_client_window(FIMContext, nil);
{$ENDIF}
end;
end;
procedure TCEFLinuxOSRIMEHandler.ConnectSignals;
begin
if Initialized then
begin
{$IFDEF LCLGTK3}
g_signal_connect_data(PGObject(@FIMContext), 'commit', TGCallback(@gtk_commit_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
g_signal_connect_data(PGObject(@FIMContext), 'preedit-start', TGCallback(@gtk_preedit_start_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
g_signal_connect_data(PGObject(@FIMContext), 'preedit-end', TGCallback(@gtk_preedit_end_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
g_signal_connect_data(PGObject(@FIMContext), 'preedit-changed', TGCallback(@gtk_preedit_changed_cb), GPointer(FPanel.Handle), nil, G_CONNECT_DEFAULT);
{$ENDIF}
{$IFDEF LCLGTK2}
g_signal_connect(G_OBJECT(FIMContext), 'commit', G_CALLBACK(@gtk_commit_cb), GPointer(FPanel.Handle));
g_signal_connect(G_OBJECT(FIMContext), 'preedit-start', G_CALLBACK(@gtk_preedit_start_cb), GPointer(FPanel.Handle));
g_signal_connect(G_OBJECT(FIMContext), 'preedit-end', G_CALLBACK(@gtk_preedit_end_cb), GPointer(FPanel.Handle));
g_signal_connect(G_OBJECT(FIMContext), 'preedit-changed', G_CALLBACK(@gtk_preedit_changed_cb), GPointer(FPanel.Handle));
{$ENDIF}
end;
end;
procedure TCEFLinuxOSRIMEHandler.Focus;
begin
if Initialized then
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_focus_in(FIMContext);
{$ENDIF}
FHasFocus := True;
end;
end;
procedure TCEFLinuxOSRIMEHandler.Blur;
begin
if Initialized then
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_focus_out(FIMContext);
{$ENDIF}
FHasFocus := False;
end;
end;
procedure TCEFLinuxOSRIMEHandler.Reset;
begin
if Initialized then
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
gtk_im_context_reset(FIMContext);
// Some input methods may not honour the reset call.
// Focusing out/in the to make sure it gets reset correctly.
if FHasFocus then
begin
gtk_im_context_focus_out(FIMContext);
gtk_im_context_focus_in(FIMContext);
end;
{$ENDIF}
end;
end;
procedure TCEFLinuxOSRIMEHandler.SetCursorLocation(X, Y: integer);
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
var
TempCurPos: TGdkRectangle;
{$ENDIF}
begin
if Initialized then
begin
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
TempCurPos.x := x;
TempCurPos.y := y;
TempCurPos.width := 0;
TempCurPos.height := 0;
gtk_im_context_set_cursor_location(FIMContext, @TempCurPos);
{$ENDIF}
end;
end;
{$IF DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
function TCEFLinuxOSRIMEHandler.FilterKeyPress(aEvent : PGdkEventKey) : boolean;
begin
if Initialized then
Result := gtk_im_context_filter_keypress(FIMContext, aEvent);
end;
{$ENDIF}
end.

View File

@@ -2,7 +2,7 @@
"UpdateLazPackages" : [
{
"ForceNotify" : true,
"InternalVersion" : 769,
"InternalVersion" : 770,
"Name" : "cef4delphi_lazarus.lpk",
"Version" : "139.0.28"
}