秋·风

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

【原创】修复lazarus trunk在GTK3下Synedit只能输入2个中文字符的Bug

近日在lazarus trunk使用GTK3时发现Synedit(IDE代码编辑器也是用synedit)只能输入2个中文字符,经排查,找到了解决方法。
问题原因:
当中文输入时执行到DeliverIMCommit的:
UTF8Char := AStr;
AStr原来是正常的多字词组赋值给UTF8Char时变为最多2个中文,如:AStr:='中华人民共和国',赋值给UTF8Char后变为'中华#$E4';
排查发现
TUTF8Char的定义:

  TUTF8Char = String[7]; // UTF-8 character is at most 6 bytes plus a #0

就是这个定义决定了TUTF8Char不能保存多于两个中文。

procedure TGtk3Widget.DeliverIMCommit(const AStr: string);
var
  UTF8Char: TUTF8Char;
  CharMsg: TLMChar;
begin
  if (AStr = '') or not Assigned(LCLObject) then
    exit;

  {$IFDEF GTK3DEBUGKEYPRESS}
  writeln('TGtk3Widget.DeliverIMCommit ', dbgsName(LCLObject), ' str="', AStr, '"');
  {$ENDIF}
  UTF8Char := AStr;
  if LCLObject.IntfUTF8KeyPress(UTF8Char, 1, False) then
    exit;

  FillChar(CharMsg{%H-}, SizeOf(CharMsg), 0);
  CharMsg.Msg := CN_CHAR;
  CharMsg.CharCode := Word(AStr[1]);
  NotifyApplicationUserInput(LCLObject, PLMessage(@CharMsg)^);

  if DeliverMessage(CharMsg, True) <> 0 then
    exit;

  CharMsg.Msg := LM_CHAR;
  NotifyApplicationUserInput(LCLObject, PLMessage(@CharMsg)^);
  DeliverMessage(CharMsg, True);
end;

修复方法:
将输入的AStr字符逐个LCLObject.IntfUTF8KeyPress(UTF8Char, 1, False)就可以。
打开/lazarus/lcl/interfaces/gtk3/gtk3widgets.pas这个单元。
将procedure TGtk3Widget.DeliverIMCommit(const AStr: string)改为以下代码,然后重构lazarus就可以。

procedure TGtk3Widget.DeliverIMCommit(const AStr: string);
var
  UTF8Char: TUTF8Char;
  CharMsg: TLMChar;
  i:Integer;
begin
  if (AStr = '') or not Assigned(LCLObject) then
    exit;

  {$IFDEF GTK3DEBUGKEYPRESS}
  writeln('TGtk3Widget.DeliverIMCommit ', dbgsName(LCLObject), ' str="', AStr, '"');
  {$ENDIF}

  for i:=1 to UTF8Length(AStr)  do
  begin
    UTF8Char:=utf8Copy(AStr,i,1);
    if LCLObject.IntfUTF8KeyPress(UTF8Char, 1, False) then
      Continue;

    FillChar(CharMsg{%H-}, SizeOf(CharMsg), 0);
    CharMsg.Msg := CN_CHAR;
    CharMsg.CharCode := ord(UTF8Char[1]);
    NotifyApplicationUserInput(LCLObject, PLMessage(@CharMsg)^);

    DeliverMessage(CharMsg, True);

    CharMsg.Msg := LM_CHAR;
    NotifyApplicationUserInput(LCLObject, PLMessage(@CharMsg)^);
    DeliverMessage(CharMsg, True);
  end;
end; 

 修复前:

image

 修复后:

557d2800f6242b3b1c3602d1b887c3a7

2026-08-15 昨天将我的修复方法提交官方后,今天官方已修复,思路和我和差不多:

procedure TGtk3Widget.DeliverIMCommit(const AStr: string);
var
  UTF8Char: TUTF8Char;
  CharMsg: TLMChar;
  APos, ALen, CharLen: Integer;
begin
  if (AStr = '') or not Assigned(LCLObject) then
    exit;

  {$IFDEF GTK3DEBUGKEYPRESS}
  writeln('TGtk3Widget.DeliverIMCommit ', dbgsName(LCLObject), ' str="', AStr, '"');
  {$ENDIF}

  ALen := Length(AStr);
  APos := 1;
  while APos <= ALen do
  begin
    CharLen := UTF8CodepointSize(@AStr[APos]);
    if (CharLen <= 0) or (APos + CharLen - 1 > ALen) then
      exit;
    UTF8Char := Copy(AStr, APos, CharLen);
    Inc(APos, CharLen);

    if not Assigned(LCLObject) then
      exit;
    if LCLObject.IntfUTF8KeyPress(UTF8Char, 1, False) then
      continue;

    FillChar(CharMsg{%H-}, SizeOf(CharMsg), 0);
    CharMsg.Msg := CN_CHAR;
    CharMsg.CharCode := Word(UTF8Char[1]);
    NotifyApplicationUserInput(LCLObject, PLMessage(@CharMsg)^);

    if DeliverMessage(CharMsg, True) <> 0 then
      continue;

    CharMsg.Msg := LM_CHAR;
    NotifyApplicationUserInput(LCLObject, PLMessage(@CharMsg)^);
    DeliverMessage(CharMsg, True);
  end;
end;

 

posted on 2026-08-14 09:26  秋·风  阅读(31)  评论(0)    收藏  举报