Lazars CEF不使用osr模式输入中文

news/2025/11/28 11:41:46/文章来源:https://www.cnblogs.com/jwjss/p/19281636

uses
Classes, SysUtils,LCLType,StdCtrls,Controls
,uCEFTypes,uCEFInterfaces,uCEFChromium
,Gtk2Globals,gtk2, glib2, gdk2, gtk2proc, gtk2int,pango;
type
PDisplay = Pointer; // X11 的 Display*
KeySym = Cardinal; // 通常 32 位无符号整数
Tmyclass = class
procedure Chromium1PreKeyEvent(Sender: TObject;
const browser: ICefBrowser; const event: PCefKeyEvent;
osEvent: TCefEventHandle; out isKeyboardShortcut: Boolean; out Result: Boolean
);
procedure Button1Click(Sender: TObject);
end;

function gdk_x11_display_get_xdisplay(display: PGdkDisplay): PDisplay; cdecl; external 'libgdk-x11-2.0.so.0';
function XKeycodeToKeysym(d: PDisplay; keycode: Byte; index: Longint): KeySym; cdecl; external 'libX11.so.6';

const
EVENTFLAG_SHIFT_DOWN = $00000002;
EVENTFLAG_CONTROL_DOWN = $00000004;
EVENTFLAG_ALT_DOWN = $00000008;
EVENTFLAG_CAPS_LOCK_ON = $00000020;
EVENTFLAG_SHIFTKEY = 1 shl 1;
EVENTFLAG_CONTROLKEY = 1 shl 2;
EVENTFLAG_ALTKEY = 1 shl 3;
var
InSignalKeyC:gulong;//Linux输入完成信号id
IsEn:Boolean;
Chromium1: TChromium;
Button1: TButton;
CEFShow:TWinControl;
myclass:Tmyclass;
cefkey:TCefKeyEvent;
//切换中英文用
FShiftPressed: Boolean = False;
FControlPressed: Boolean = False;
FAltPressed: Boolean = False;
FShiftPressedAlone: Boolean; // 新增:标记 Shift 是否被单独按下
FInputing:Boolean;//正在输入中文

procedure intInput(InChromium: TChromium;InButton: TButton;InCEFShow:TWinControl);
procedure outInput ;
implementation

//----- TCefKeyEvent转PGdkEventKey---------
function CefKeyEventToGdkEventKey(const CefEv: TCefKeyEvent): PGdkEventKey;
var
KeySym: guint;
Modifiers: guint;
// === 工具函数 ===
function NewGdkEventKey: PGdkEventKey;
begin
New(Result);
FillChar(Result^, SizeOf(TGdkEventKey), 0);
end;
procedure FreeGdkEventKey(var Ev: PGdkEventKey);
begin
if Assigned(Ev) then
begin
Dispose(Ev);
Ev := nil;
end;
end;
function GetCurrentEventTime: guint32;
begin
// GTK 要求时间戳是毫秒(X11 时间戳单位)
Result := guint32(GetTickCount64); // Lazarus 提供,跨平台
end;
function GetX11Display: PDisplay;
begin
Result := gdk_x11_display_get_xdisplay(gdk_display_get_default());
end;
function KeyCodeToKeysym(KeyCode: Integer): guint;
var
Disp: PDisplay;
begin
Result := 0;
Disp := GetX11Display;
if Assigned(Disp) and (KeyCode >= 0) and (KeyCode <= 255) then
begin
Result := XKeycodeToKeysym(Disp, Byte(KeyCode), 0);
end;
end;

begin
Result := NewGdkEventKey;
if not Assigned(Result) then Exit;

// 1. 事件类型
case CefEv.kind of
KEYEVENT_KEYDOWN, KEYEVENT_RAWKEYDOWN:
Result^._type := GDK_KEY_PRESS;
KEYEVENT_KEYUP:
Result^._type := GDK_KEY_RELEASE;
else
FreeGdkEventKey(Result);
Exit;
end;

// 2. 时间戳
Result^.time := GetCurrentEventTime;

// 3. keycode 和 keyval(使用 X11)
Result^.hardware_keycode := guint16(CefEv.native_key_code);
KeySym :=KeyCodeToKeysym(CefEv.native_key_code);
Result^.keyval := KeySym;

// 4. 修饰键(Modifiers)
Modifiers := 0;
if (CefEv.modifiers and EVENTFLAG_SHIFT_DOWN) <> 0 then
Modifiers := Modifiers or GDK_SHIFT_MASK;
if (CefEv.modifiers and EVENTFLAG_CONTROL_DOWN) <> 0 then
Modifiers := Modifiers or GDK_CONTROL_MASK;
if (CefEv.modifiers and EVENTFLAG_ALT_DOWN) <> 0 then
Modifiers := Modifiers or GDK_MOD1_MASK; // Alt = MOD1
if (CefEv.modifiers and EVENTFLAG_CAPS_LOCK_ON) <> 0 then
Modifiers := Modifiers or GDK_LOCK_MASK;

Result^.state := Modifiers;

// 5. 其他字段(可选)
Result^.length := 0;
Result^._string := nil;
Result^.window := nil;
Result^.send_event := 0;
Result^.group := 0;
end;
//-------- TCefKeyEvent转PGdkEventKey----------------

//----输入法输入完成事件--
procedure OnInputMethodCommitString({%H-}AContext: PGtkIMContext; AText: PChar; Data: gPointer); cdecl;
var
temp:String;
IMStr:widestring;
JS: UTF8String;
begin
temp := {%H-}UTF8Decode(AText); //输入的字符
temp:=StringReplace(temp, '\', '\\', [rfReplaceAll]);
temp:=StringReplace(temp, '"', '\"', [rfReplaceAll]);
IMStr := {%H-}temp;
if IMStr <> '' then
begin
JS := Format(
'var el=document.activeElement;' +
'if(el&&(el.tagName==="INPUT"||el.tagName==="TEXTAREA"||el.isContentEditable)){' +
' if(el.setSelectionRange){' +
' var start=el.selectionStart, end=el.selectionEnd, val=el.value;' +
' el.value=val.substring(0,start)+%s+val.substring(end);' +
' el.setSelectionRange(start+%d, start+%d);' +
' } else if(el.isContentEditable){' +
' document.execCommand("insertText",false,%s);' +
' }' +
'}',
[QuotedStr(IMStr), Length(IMStr), Length(IMStr), QuotedStr(IMStr)]
);
end;
im_context_use := False;
//js方式插入文本
Chromium1.ExecuteJavaScript(
JS{%H-}, '', Chromium1.Browser.FocusedFrame, 0);
end;

//判断是否还在输入中
function HasPreeditText: Boolean;
var
preeditStr: Pgchar; // 实际字符串指针
attrs: PPangoAttrList; // 属性列表指针(我们不用)
cursorPos: gint; // 光标位置(我们不用)
begin
// 初始化输出参数
preeditStr := nil;
attrs := nil;
cursorPos := 0;

// 调用 GTK 函数:注意 str 参数是 @preeditStr(取地址)
gtk_im_context_get_preedit_string(
im_context,
@preeditStr, // 传地址(PPgchar = ^Pgchar)
attrs, // 可传 nil,但 safest 是传指针
@cursorPos
);
try
// 检查字符串是否非空
Result := (preeditStr <> nil) and (preeditStr^ <> #0);
finally
// 释放内存
g_free(preeditStr);
if attrs <> nil then
pango_attr_list_unref(attrs);

end;
end;

{Tmyclass}
//CEF输入事件
procedure Tmyclass.Chromium1PreKeyEvent(Sender: TObject;
const browser: ICefBrowser; const event: PCefKeyEvent;
osEvent: TCefEventHandle; out isKeyboardShortcut: Boolean; out Result: Boolean
);
var
gdkEvent: PGdkEventKey;
keyCode: Integer;
isModifierKey: Boolean;
pr: TGdkRectangle;
MouseP:TPoint;
ClientP: TPoint;
begin
Result := False;//默认交给cef处理
keyCode := event^.windows_key_code;


//按Shift切换中英文
//方法一
isKeyboardShortcut := False;

if not Assigned(event) then
Exit;

// 判断是否为修饰键(Shift/Ctrl/Alt)
isModifierKey := (keyCode = VK_SHIFT) or (keyCode = VK_CONTROL) or (keyCode = VK_MENU);

// 更新修饰键状态(仅用于状态跟踪)
case keyCode of
VK_SHIFT: FShiftPressed := (event^.kind = KEYEVENT_RAWKEYDOWN);
VK_CONTROL: FControlPressed := (event^.kind = KEYEVENT_RAWKEYDOWN);
VK_MENU: FAltPressed := (event^.kind = KEYEVENT_RAWKEYDOWN);
end;

// 如果是 Shift 按下
if (keyCode = VK_SHIFT) and (event^.kind = KEYEVENT_RAWKEYDOWN) then
begin
// 假设此时是“单独按下 Shift”(后续可能被推翻)
FShiftPressedAlone := True;
end

// 如果在 Shift 按下期间,按下了其他非修饰键,则取消“单独 Shift”状态
else if (event^.kind = KEYEVENT_RAWKEYDOWN) and (not isModifierKey) then
begin
if FShiftPressed then
FShiftPressedAlone := False;
end

// 如果是 Shift 释放
else if (keyCode = VK_SHIFT) and (event^.kind = KEYEVENT_KEYUP) then
begin
if FShiftPressedAlone then
begin
// 确保没有 Ctrl/Alt 被按下(虽然理论上已由 FShiftPressedAlone 保证)
if (not FControlPressed) and (not FAltPressed) then
begin
if not HasPreeditText then
begin
Button1Click(Button1); // 用按钮切换中英文

Result := True; // 阻止事件传递(可选)
end;
end;
end;

// 重置标志
FShiftPressedAlone := False;
Exit;
end;

//---开始输入---
if not IsEn then//不是英文将信息发送到输入法,是则直接交给cef
begin
//设置输入框弹出位置
mouseP:=Mouse.CursorPos;
ClientP := CEFShow.ScreenToClient(MouseP);
pr.height:=0;
pr.width:=0;
pr.x:=ClientP.X;
pr.y:=ClientP.y;
gtk_im_context_set_cursor_location(IM_Context, @pr);//设置输入框位置--重点
//中文输入 发送给输入法
gdkEvent := CefKeyEventToGdkEventKey(Event^);
// 判断是否为需要特殊处理的键
if (keyCode = VK_BACK) or (keyCode = VK_DELETE) or
(keyCode = VK_RETURN) or (keyCode = VK_LEFT) or
(keyCode = VK_RIGHT) or (keyCode = VK_UP) or (keyCode = VK_DOWN) then
begin
if HasPreeditText then
begin
// 有选词框 → 交给 IME 处理
Result := True;
gtk_im_context_filter_keypress(im_context, gdkEvent);
end;
end
else
begin
//gtk_im_context_filter_keypress返回真则交给cef;返回假,阻止事件继续传播,Chromium 不会处理此键事件
Result := not (gtk_im_context_filter_keypress(im_context, gdkEvent));
if event^.character='.' then Result:=True;//中文输入时输入.会出现返回.。的问题
if event^.character='''' then Result:=True;//中文输入时输入.会出现返回.。的问题
end;
Dispose(gdkEvent);
end;
end;

procedure Tmyclass.Button1Click(Sender: TObject);
begin
if IsEn then
begin
IsEn:=False;
Button1.Caption:='中文';
end
else
begin
IsEn:=True;
Button1.Caption:='英文';
end;
end;

 

procedure intInput(InChromium: TChromium;InButton: TButton;InCEFShow:TWinControl);
begin
Chromium1:=InChromium;
Button1:=InButton;
CEFShow:=InCEFShow;
Chromium1.OnPreKeyEvent:=@myclass. Chromium1PreKeyEvent;
Button1.OnClick:=@myclass.Button1Click;
gtk_im_context_focus_in(IM_Context);
InSignalKeyC:= g_signal_connect(IM_Context, 'commit', TGTKSignalFunc(@OnInputMethodCommitString),Nil);
IsEn:=False;
FInputing:=False;
Button1.ShowHint:=True;
Button1.Hint:='保持启动中文输入法'+#10#13+'并切换到中文输入状态'+#10#13+'用Shift切换中英文';
Button1.Caption:='中文';
end;

procedure outInput ;
begin
g_signal_handler_disconnect(im_context,InSignalKeyC);
end;

使用方法。

procedure TForm1.FormCreate(Sender: TObject);
begin

//加这行

IputCN.intInput(Chromium1,Button1,CEFLinkedWindowParent1);
end;

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/979513.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 进程管理实战:检查与启动EXE程序的完整指南 - 详解

C# 进程管理实战:检查与启动EXE程序的完整指南 - 详解2025-11-28 11:40 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; d…

2025年北京财税公司联系电话完整汇总:覆盖重点区域官方联系方式与高效合作指引

本文基于2025年行业公开数据和权威第三方报告,结合推荐对象参考内容,从专业资质、服务经验、团队实力等维度筛选5个推荐对象,旨在为北京财税公司联系电话领域提供可靠联系方案。根据中国注册会计师协会发布的《2024…

化学方程式配平(第33次CCF计算机软件能力认证 第3题)题解 高斯消元

题目链接:https://www.acwing.com/problem/content/5724/ 前置练习:P3389 【模板】高斯消元法 解题思路: 首先根据字符串构造矩阵。 假设构造的是一个 \(n \times m\) 的矩阵。 很明显,秩最大是 \(\min(n,m)\)。 要…

通过自动DNS代理实现SSL证书免费申请

申请域名SSL证书时,无论是付费还是免费的,都需要验证域名。今天教大家个方法,用DNS代理就能自动验证域名,只需要首次设置,以后再申请就不需要再设置了,最终能够实现证书申请自动化。 自动DNS代理验证步骤1、申请…

2025年11月北京财税公司推荐:权威榜单与专业选择指南

在当今复杂的经济环境下,北京的中小微企业普遍面临财税合规压力增大和运营成本上升的双重挑战。根据北京市税务局发布的最新数据,截至2025年第三季度,北京市小微企业数量已突破百万家,其中超过八成企业需要专业财税…

2025年质量好的温拌剂路面材料品牌厂家排行榜

2025年质量好的温拌剂路面材料品牌厂家排行榜行业背景与市场趋势随着全球环保意识的提升和道路建设技术的不断进步,温拌剂路面材料作为一种环保、节能的新型筑路材料,正逐渐成为道路建设领域的主流选择。相比传统热拌…

2025年口碑好的卫浴全屋五金/橱衣柜全屋五金厂家推荐及选购参考榜

2025年口碑好的卫浴全屋五金/橱衣柜全屋五金厂家推荐及选购参考榜行业背景与市场趋势随着中国家居消费升级和品质生活需求的不断提升,全屋五金行业正迎来前所未有的发展机遇。2024-2025年,卫浴五金和橱衣柜五金市场预…

广开海鲜舫联系方式:了解餐厅背景与预订须知

一、官方联系方式 联系电话:0532-88895225 二、使用建议与提醒 建议提前通过电话确认营业时间与座位情况,避免高峰期等候。 可根据个人口味偏好与工作人员沟通菜品选择,餐厅注重食材新鲜度。 注意餐厅位置可能因分店…

【实验】envoy 中的“Failover”(故障转移)

【实验】envoy 中的“Failover”(故障转移)作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!cnblogs博客 zhihu Github 公众号:一本正经的瞎扯先说结论: envoy 中转发时一定要配置 retry_policy.num_re…

2025年大王椰板材公司:高端环保板材市场格局与产业链竞争力深度解析

本文将从产业链竞争力视角切入,为读者剖析大王椰板材公司在高端环保板材领域的战略定位与发展潜力。作为行业国家标准制定者之一,其产业布局与技术实力构成分析的核心维度。 大王椰板材公司,即杭州大王椰控股集团有…

【IEEE出版 | EI检索】2025电子信息、计算机与空天遥感国际会议(EICARS 2025)

2025电子信息、计算机与空天遥感国际会议(EICARS 2025)将于2025年12月12-14日在中国江门举行,本次会议由五邑大学主办。【IEEE官方列表会议、五邑大学主办、已有ISBN号!】 2025电子信息、计算机与空天遥感国际会议…

2025年一体化灌溉泵站供货厂家权威推荐榜单:一体化智能泵站‌/污水提升一体化泵站‌/玻璃钢一体化泵站源头厂家精选

在农业现代化与节水灌溉政策推动下,一体化灌溉泵站因其模块化设计、智能控制与高效节水特性,市场需求显著增长。行业数据显示,2025年中国农业灌溉泵站市场规模预计突破80亿元,年均增长率稳定在12%-15%。为帮助用户…

2025年frpp增强管定做厂家权威推荐榜单:frpp管材/PPH热熔弯头/pp工业管材源头厂家精选

在工业管道领域,FRPP(玻纤增强聚丙烯)管道因其优异的耐腐蚀性能、高强度重量比和长使用寿命,已成为化工、电力、冶金等行业的重要选择。据行业调研显示,中国工业塑料管道市场规模预计在2025年将达到约680亿元,年…

2025 年 11 月防水连接器厂家实力推荐榜:RJ45/圆形防水RJ45/D型防水RJ45插座,工业级耐用与密封防护优选

2025 年 11 月防水连接器厂家实力推荐榜:RJ45/圆形防水RJ45/D型防水RJ45插座,工业级耐用与密封防护优选 随着工业物联网和智能制造技术的快速发展,防水连接器作为关键电子组件,在工业自动化、户外通信设备、轨道交…

广开尚宴联系方式:了解青岛海鲜餐饮的参考信息

一、官方联系方式 广开尚宴电话号:0532-55572388 二、使用建议与提醒 在联系广开尚宴时,建议提前确认营业时间,避免高峰时段等待。可通过电话咨询菜品详情或预订服务,但需注意个人信息安全。选择餐饮服务时应结合自…

2025上海留学中介哪家排名好

2025上海留学中介哪家排名好一、2025年上海留学中介如何选择作为从事国际教育规划工作超过十年的资深顾问,我经常被学生和家长询问:"2025年上海地区哪家留学中介更值得信赖?"这个问题的答案并非绝对,但根…

2025青岛香港留学中介哪家好

2025青岛香港留学中介哪家好一、2025青岛香港留学中介选择指南作为从业八年的国际教育规划师,我经常被青岛地区的学生和家长问及:“2025年申请香港留学,哪家中介更可靠?”根据《2025中国留学中介口碑白皮书》数据显…

2025 年 11 月激振器厂家权威推荐榜:DF/HE/LE/ZDQ/RDQ/JR/BE/UE/KWD/G/ML/MV/DVE全系列型号深度解析与高效选型指南

2025 年 11 月激振器厂家权威推荐榜:DF/HE/LE/ZDQ/RDQ/JR/BE/UE/KWD/G/ML/MV/DVE全系列型号深度解析与高效选型指南 行业背景与发展现状 激振器作为工业振动设备的核心部件,广泛应用于矿山、煤炭、冶金、电力等行业。…

2025年知名的塑磁组件厂家最新推荐权威榜

2025年知名的塑磁组件厂家推荐权威榜开篇:塑磁组件行业背景与市场趋势随着全球制造业向智能化、高效化方向快速发展,塑磁组件作为电机、传感器、汽车电子等关键领域的核心部件,市场需求持续增长。据行业研究数据显示…

2025年机械手柔性夹爪厂家推荐:苏州柔触机器人科技有限公司引领行业新标准

在工业自动化与智能制造深度融合的当下,机械手柔性夹爪作为关键末端执行器,正成为解决异形、易碎、高表面要求物料抓取难题的核心装备。面对市场上众多“机械手柔性夹爪厂家”“供应商”或“品牌”的选择,用户更关注…