【原创】修复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;
修复前:

修复后:

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;

浙公网安备 33010602011771号