秋·风

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  276 随笔 :: 0 文章 :: 305 评论 :: 19万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

虽然cef4delphi在GTK3可以使用中文输入,但lazarus支持GTK3不完善,且还要安装GTK3才能使用。为尽量可能利用已有的gtk2环境,在网上没有相关资料可以参考的情况下,经多日研究,今天(2025-01-22)终于修复了cef4delphi(lazarus)的SimpleOSRBrowser for GTK2不能输入中文的问题。
    我这个方法不是最优,但可以暂时解决中文输入存在的困扰,如果你有更好的方法也请与我分享,谢谢!
2025-02-03重要提醒:
实现这个方法的前提要按https://www.cnblogs.com/qiufeng2014/p/15722762.html修复linux中文输入的Bug,否则不会响应LM_IM_COMPOSITION消息,无法实现中文输入。

注意:本方法适用于CEF OSR模式。
2025-01-23:
输入框显示在鼠标的位置

2025-01-22 21:45更新:
早上代码贴少了,现已补充完成,请重新测试。

测试环境:

操作系统:Linux raspberrypi 6.6.62+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.6.62-1+rpt1 (2024-11-25) aarch64 GNU/Linux(树莓派 64位)/GTK2
CPU:aarch64
cef4delphi版本:131.3.5
lazarus 4.0RC2 /fpc 3.3.1

修改后的CEF4Delphi-131.3.5下载:

链接: https://pan.baidu.com/s/1VcWBn-zEpBQ0lOiJA9Ik2w?pwd=gd6k 提取码: gd6k
压缩包里不包含CEF各平台的二进制文件,可根据需要自行下载:
https://github.com/salvadordf/CEF4Delphi/tree/131.0.6778.265

修复步骤:
一、TBufferPanel增加
1、WMIMComposition--处理中文
2、FOnIMETextChange: TNotifyEvent--当中文输入时通知应用文字有变化
3、FIMContext_string---保存变化的文字
uCEFBufferPanel.pas
复制代码
interface

uses
  {$IFDEF DELPHI16_UP}
  {$IFDEF MSWINDOWS}Winapi.Windows, Winapi.Messages, Vcl.ExtCtrls, Vcl.Controls, Vcl.Graphics, WinApi.Imm, {$ENDIF}
  System.Classes, System.SyncObjs, System.SysUtils, Vcl.Forms,
  {$ELSE}
    {$IFDEF MSWINDOWS}Windows, imm, {$ENDIF} Classes, Forms, Controls, Graphics,
    {$IFDEF FPC}
    Dialogs,
    {$ifdef LCLgtk2}
    gdk2,Gtk2Globals,
    Messages,
    {$endif}
    LCLProc, LCLType, LCLIntf, LResources, LMessages, InterfaceBase,{$IFDEF MSWINDOWS}Win32Extra,{$ENDIF}
    {$ELSE}
    Messages,
    {$ENDIF}
    ExtCtrls, SyncObjs, SysUtils,
复制代码
  TBufferPanel = class(TCustomPanel)
    protected
      {$ifdef LCLGTK2}
      FIMContext_string       :string;
      FOnIMETextChange        : TNotifyEvent;
      {$endif}
      FScanlineSize            : integer;
      FTransparent             : boolean;
      {$ELSE}
      FSyncObj                 : TCriticalSection;
      {$ENDIF}
      {$ifdef LCLGTK2}
      procedure Change; virtual;
      procedure WMIMComposition(var Message: TMessage); message LM_IM_COMPOSITION;
      {$endif}
      procedure CreateSyncObj;
      procedure Paint; override;
      {$ifdef LCLGTK2}
      procedure SetIMContext_string(aValue : String);
      {$endif}
      {$IFDEF MSWINDOWS}
      procedure CreateParams(var Params: TCreateParams); override;
      procedure   DrawOrigPopupBuffer(const aSrcRect, aDstRect : TRect);
      {$ifdef LCLGTK2}
      property IMContext_string :string read FIMContext_string write SetIMContext_string;
      {$endif}
      /// <summary>
      /// Returns the scanline size.
      /// </summary>
      property ScanlineSize              : integer                   read FScanlineSize;
复制代码
      property OnPointerUpdate           : TOnHandledMessageEvent    read FOnPointerUpdate           write FOnPointerUpdate;
      {$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.
      /// </summary>
      property OnPaintParentBkg          : TNotifyEvent              read FOnPaintParentBkg          write FOnPaintParentBkg;
      {$ifdef LCLGTK2}
      property OnIMETextChange            : TNotifyEvent              read FOnIMETextChange            write FOnIMETextChange;
      {$endif}
      /// <summary>
复制代码
复制代码
{$ifdef LCLGTK2}
procedure TBufferPanel.SetIMContext_string(aValue : String);
begin
  if (FIMContext_string <> aValue) then
  begin
      FIMContext_string := aValue;
  end;
end;

procedure TBufferPanel.Change;
begin
  Changed;
  if Assigned(FOnIMETextChange) then FOnIMETextChange(Self);
end;

procedure TBufferPanel.WMIMComposition(var Message: TMessage);
var
  IMStr:string;
begin
  if Message.WParam=GTK_IM_FLAG_COMMIT then
  begin
    FIMContext_string:='';
    IMStr:=UTF8Decode(pchar(Message.LParam)); //输入的字符
    if IMStr<>'' then
    begin
      FIMContext_string:= IMStr;
      Change;
    end;
  end;
  im_context_use:=false;
  inherited;
end;
{$endif}

procedure TBufferPanel.CreateSyncObj;
begin
  {$IFDEF MSWINDOWS}
  FSyncObj := CreateMutex(nil, False, nil);
  {$ELSE}
  FSyncObj := TCriticalSection.Create;
  {$ENDIF}
end;
复制代码

 

二、uSimpleOSRBrowser

1、uses增加Gtk2Globals
复制代码
unit uSimpleOSRBrowser;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  LCLType, ComCtrls, Types, SyncObjs, LMessages,Messages,
  pango,//
  Gtk2Globals,
  uCEFChromium, uCEFTypes, uCEFInterfaces, uCEFConstants, uCEFBufferPanel,
  uCEFChromiumEvents;

type
  { TForm1 }
复制代码
    procedure AddressEdtEnter(Sender: TObject);
  private
  protected
    FRMouseEvent     : TCefMouseEvent;
    FPopUpRect       : TRect;

2、改变function GTKKeyPress(Widget: PGtkWidget; Event: PGdkEventKey; Data: gPointer) 的行为:

复制代码
function GTKKeyPress(Widget: PGtkWidget; Event: PGdkEventKey; Data: gPointer) : GBoolean; cdecl;
var
  TempCefEvent : TCefKeyEvent;
  EventString: PChar; // GTK1 and GTK2 workaround
  pr: TGdkRectangle;
begin
  //设置输入框弹出位置
  pr.height:=0;
  pr.width:=0;
  pr.x:=form1.FRMouseEvent.x;
  pr.y:=form1.FRMouseEvent.y;// form1.AddressEdt.Height;

  gtk_im_context_focus_in(IM_Context);//设置焦点-激活输入法---重点
  gtk_im_context_set_cursor_location(IM_Context, @pr);//设置输入框位置--重点
  gdk_event_key_get_string(Event, EventString{%H-}); //取输入的字符

  TempCefEvent.windows_key_code:=GdkEventToWindowsKeyCode(Event);
  TempCefEvent.native_key_code:=Event^.hardware_keycode;
  TempCefEvent.modifiers:=Event^.state;
  TempCefEvent.character:=EventString^;

  if (Event^._type = GDK_KEY_PRESS) then
  begin
    TempCefEvent.kind := KEYEVENT_RAWKEYDOWN;
    Form1.Chromium1.SendKeyEvent(@TempCefEvent);
    TempCefEvent.kind := KEYEVENT_CHAR;
    Form1.Chromium1.SendKeyEvent(@TempCefEvent);
  end
  else
  begin
    TempCefEvent.kind := KEYEVENT_KEYUP;
    Form1.Chromium1.SendKeyEvent(@TempCefEvent);
  end;
  Result := true;
end;
复制代码
复制代码
procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  TempEvent : TCefMouseEvent;
begin
  FRMouseEvent.x:=x;
  FRMouseEvent.y:=y;
  TempEvent.x         := x;
  TempEvent.y         := y;
  TempEvent.modifiers := getModifiers(Shift);
  DeviceToLogical(TempEvent, Panel1.ScreenScale);
  Chromium1.SendMouseMoveEvent(@TempEvent, False);
end;
复制代码

3、添加Panel1IMETextChange(Sender: TObject)
将中文推送到当前的输入框

复制代码
procedure TForm1.Panel1IMETextChange(Sender: TObject);
var
  i:integer;
  ims:widestring;
  TempCefEvent : TCefKeyEvent;
begin
  ims:=Panel1.imcontext_string;
  for i:=1 to length(ims) do
  begin
    TempCefEvent.character:=ims[i];//取中文字符
    TempCefEvent.kind := KEYEVENT_RAWKEYDOWN;
    Chromium1.SendKeyEvent(@TempCefEvent);
    TempCefEvent.kind := KEYEVENT_CHAR;
    Chromium1.SendKeyEvent(@TempCefEvent);//推送单个中文字符到cef当前输入框
  end;
  Panel1.imcontext_string:='';
end;
复制代码

按以上修改后重新编译cef4delphi控件及应用就可以输入中文。
存在问题:中文输入法在窗口左上角

修复后的中文输入截图:

 新修正的可以在鼠标的位置显示输入框:

 

posted on   秋·风  阅读(159)  评论(14编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示