Author Topic: [Qt5/Qt6] JPSupport-Qt: Japanese (CJK) IME support for SynEdit - seeking feedbac  (Read 2593 times)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12760
  • Debugger - SynEdit - and more
    • wiki
I must confess, I missed your reply on the patch. I will check it in a bit.

I pinged Zeljko on the issue. (just now)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12760
  • Debugger - SynEdit - and more
    • wiki
Ok, Zeljko  is aware but currently away. He may be able to check back with this in a week or two at the earliest.

shortcut

  • New Member
  • *
  • Posts: 16
That's great to hear, thank you for checking. No rush at all — I'll wait for Zeljko whenever he has time to look at it.

zeljko

  • Hero Member
  • *****
  • Posts: 2016
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Can you point me to the Qt6 bindings patches ?

shortcut

  • New Member
  • *
  • Posts: 16
Hi zeljko,

Sure. The Qt6 bindings part is a standalone patch against fixes_4 (generated from 4cab00d2), touching only lcl/interfaces/qt6:

https://github.com/53jouhikone-source/JPSupport/blob/main/JPSupport-Qt/patches/upstream/jpsupport-qt-qt6-bindings.patch

It applies cleanly to a pristine tree (checked with `git apply --check` and `patch -p1 --dry-run`). It is the same content my build script applies, just as a plain unified diff so it is easy to review. The header of the patch file lists what each hunk does.

On your earlier concern about C++ binding changes and libQt6Pas: the change is purely additive. It adds six small read-only C accessors for QInputMethodEvent::attributes() (count, type, start, length, backgroundColor, bold) in qevent_c.h/.cpp, plus their declarations in qt62.pas. No existing exported function is changed or removed. The reason is that QInputMethodEvent::attributes(), which carries the clause (bunsetsu) boundaries and the cursor position from the IME, is not exposed by the current bindings at all, and I could not find a way to get it without a new accessor. Of course, libQt6Pas has to be rebuilt to get the new symbols, and an old libQt6Pas will fail to link with the patched LCL; I understand that is exactly the pain point you mentioned, so I am happy to adjust this to whatever fits your release process.

The qtwidgets.pas hunk also depends on the new LM_IM_SET_PREEDIT / LM_IM_QUERY_CARET_POS messages and TIMEPreeditInfo in lcl/lmessages.pp, which are in the shared (Qt5/Qt6) patch and not in this file. Controls that do not handle these messages (e.g. TEdit) keep the previous native Qt behaviour. One unrelated fix is included in SlotInputMethod: multi-character commit strings were truncated because a UTF-16 WideString was decoded as UTF-8.

I have tested Qt6 with Fcitx5+Mozc on a Raspberry Pi 4 (Debian 12, aarch64, Lazarus 4.8) and a Raspberry Pi 5 (Ubuntu 24.04 in Docker). With IBus+Mozc the candidate window stays at the top-left, which looks like a Qt/IBus issue (ibus/ibus#2391) rather than something in this patch.

Please let me know if you would like it split differently, or if you would prefer another way to deliver the libQt6Pas part.

Thanks,
Shortcut

zeljko

  • Hero Member
  • *****
  • Posts: 2016
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Ok, I'll add changes to the c bindings, methods in qt62.pas will be available with some define like useqmethodinput or similar, so change can go into fixes_5 , together with c bindings change. But i will not add your patches to.the qtwidgets.pas because i did some work there on fcix5 support (chinese) and it used to work, so you must reevaluate your patches for qtwidgets first.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12760
  • Debugger - SynEdit - and more
    • wiki
OK, started looking at the SynEdit part. Great start, but needs some work. (and I need some feedback on some parts)

1) I applied and checked on the 4.n branch => but new patches should be a against Lazarus main branch please. (or a cloned git with a feature branch forked of current main)

2) COMMENTS
- Some are too verbose e.g. inside TCustomSynEdit.WMImeQueryCaretPos => that comment states the obvious. Please don't.
- Comment in front of procedure WMImeQueryCaretPos and procedure WMImeSetPreedit: To verbose / wrong place.
  => that the msg can also be sent to other-than-SynEdit => obvious: therefore to verbose
  => what QT expects: either on the dispatching end, or inside the QT dedicated class (but not here, in theory others WS can dispatch this msg too)

Not yet reviewed comments in other files => please adjust according to the above. Thanks

Also, why not place those 2 new methods, with the other existing message handlers?
(if they are indeed needed / see below)


3)

How does LM_IM_QUERY_CARET_POS differ from IMR_QUERYCHARPOSITION ?

- Yes its the current caret, for which you need to know at which char (in the pre-edit text) the caret is.
- Why "R^.Right := R^.Left + 2;" ? What if the caret is in insert mode, then it is way wider.

What is the exact spec in QT => what does QT sent, and where is the documentation for that?

4)
Where does LM_IM_SET_PREEDIT differ from GCS_COMPSTR ?


5)

Code: Pascal  [Select][+][-]
  1. FPreeditBox: TPaintBox;

No, don't. NO!

The received text is not to be "just painted over the edit".

It should be inserted into the actual text (other text in the edit should move, when the pre-text changes).

See procedure LazSynImeFull.WMImeComposition(var Msg: TMessage);
It uses a selection object to insert the text (with a special undo reason ecImeStr).

But you might also use SetTextBetweenPoints, however you must keep track of the text you inserted, so you can revert this (including reverting undo info).

For coloring you can create pairs of TSynEditMarkupSelection and TSynEditSelection. Or other markup objects including your own subclasses if that is needed.


shortcut

  • New Member
  • *
  • Posts: 16
Hi zeljko, Martin_fr,

Thanks both for the detailed feedback — very helpful.

@zeljko: Good to hear the c bindings approach (behind a define like useqmethodinput) works for you, and that it can go in together for fixes_5. Understood on qtwidgets.pas — I wasn't aware of your existing fcitx5/Chinese work there, so I'll re-test specifically against that case before resubmitting anything. Could you also point me to the branch/commit (or just describe the key points) of that existing work? That would let me test against it directly and avoid re-breaking what you already have working, rather than guessing from scratch. Once I've verified against it, I'll come back either with a revised patch that doesn't regress it, or a clearer explanation of what my patch actually needs from qtwidgets.pas if the two can coexist.

@Martin_fr: Thanks for taking the time to go through the SynEdit side in detail.

Understood, I'll rebase against main (or a feature branch off current main) for future patches.

Fair point on the comments — I'll trim the ones that just restate the obvious, and move the dispatching-related notes to wherever they actually belong (dispatcher or the QT-specific class), not SynEdit. I'll go through the other files too once this pass is done. I'll also move the two new methods next to the existing message handlers as you suggested.

Let me get back to you with the exact Qt documentation reference for what QT sends on this — I used attributes()/cursor position empirically but I should cite the actual Qt spec rather than my own interpretation. You're right that R^.Right := R^.Left + 2 is wrong for insert mode; that was a placeholder I should not have left in. I'll fix it properly once I have the caret-width logic sorted.

Same here — I'll write up a clear comparison of LM_IM_SET_PREEDIT vs GCS_COMPSTR instead of assuming it's obvious.

Point taken on TPaintBox — I understand the concern (text should actually move, not just be painted over). I'll look at LazSynImeFull.WMImeComposition as the reference and rework the preedit handling to insert into the real text with a dedicated undo reason, tracking what was inserted so it can be reverted cleanly. This is a bigger change than the rest, so it'll take me some time to get right rather than rush out an update — I'd rather come back with something that actually works your way than a quick patch that repeats the same mistake.

I'll follow up once I have concrete answers/updates on 3–5, and a re-tested qtwidgets.pas patch for zeljko.

Thanks again,
Shortcut

shortcut

  • New Member
  • *
  • Posts: 16
@Martin_fr: review item 5 (replace the TPaintBox overlay in LazSynImeQt with real text insertion) is done and tested on Qt6.

LazSynImeQt now works like LazSynImeFull on Windows: the preedit is inserted as real text (Undo/Redo locked) and marked with TSynEditMarkupSelection, and the committed text goes through ecImeStr, so one conversion is one undo step.

For the display, the focused segment uses the editor's selection colours and the other segments get a dashed underline. As far as I can tell from the fcitx5-qt source, that is what the IME asks Qt widgets to render, and KWrite looks the same. I dropped my earlier fixed light-blue colour to follow the theme. Would you prefer something else? It is a two-line change.

Patches (review snapshots against main, e5ece2347d):
https://github.com/53jouhikone-source/JPSupport/tree/main/JPSupport-Qt/patches/upstream

Not done yet: the Qt5 side. I will port it once the Qt6 side has settled. Is there anything in lazsynqtimm.pas you would like restructured before then?

shortcut

  • New Member
  • *
  • Posts: 16
Hi zeljko,

following up on my earlier reply. I did not wait for a pointer to your fcitx5/Chinese work; I found your IME commits myself (a793a2b5ad, bea692b6e7, 4b2d37f3ac, c002513a35) and compared them with our qtwidgets.pas changes.

Our changes are additive only, and they are now wrapped in {$IFDEF useqmethodinput}, with the define set in lcl/interfaces/qt5|qt6/qtdefines.inc.

- With the define OFF, the widgetset builds and links against an unmodified system libQt5Pas (the new C accessors are not referenced).
- Japanese input (fcitx5 + Mozc) in TMemo works as before with the define OFF, and also with it ON.
- I said I would re-test your Chinese case, but I could not get a working Pinyin setup here, so that part is untested. That is why I made the define: your Chinese path stays untouched when it is OFF.

Updated patches: https://github.com/53jouhikone-source/JPSupport/tree/main/JPSupport-Qt/patches/upstream

There is no hurry. Whenever your bindings are in, could you tell me if the define name and its location are fine for you?

Best regards,
Shortcut

rasberryrabbit

  • Full Member
  • ***
  • Posts: 158
I looked at QT6's bind source code, but some function bindings and declarations for atttibute processing are missing.
It would need a binding function like this.

Code: C  [Select][+][-]
  1.     #include <QInputMethodEvent>
  2.     #include <QTextFormat>
  3.      
  4.     extern "C" {
  5.      
  6.         int QInputMethodEvent_attr_count(QInputMethodEvent* event) {
  7.             return event->attributes().size();
  8.         }
  9.      
  10.         struct QInputMethodEvent_Attr {
  11.             int type;
  12.             int start;
  13.             int length;
  14.             bool hasUnderline;
  15.             unsigned int color; // RGB
  16.         };
  17.      
  18.         int QInputMethodEvent_attr_at(QInputMethodEvent* event, int index, QInputMethodEvent_Attr* result) {
  19.             auto attrs = event->attributes();
  20.             if (index >= 0 && index < attrs.size()) {
  21.                 const auto& attr = attrs.at(index);
  22.                 result->type = attr.type;
  23.                 result->start = attr.start;
  24.                 result->length = attr.length;
  25.                
  26.                 if (attr.type == QInputMethodEvent::TextFormat) {
  27.                     QTextFormat fmt = attr.value.value<QTextFormat>();
  28.                     if (fmt.isCharFormat()) {
  29.                         QTextCharFormat cfmt = fmt.toCharFormat();
  30.                         result->hasUnderline = cfmt.fontUnderline();
  31.                         result->color = cfmt.foreground().color().rgb();
  32.                     }
  33.                 } else {
  34.                    result->hasUnderline = false;
  35.                    result->color = 0;
  36.                 }
  37.               return 1;
  38.             } else
  39.                 return 0;
  40.         }
  41.      
  42.     } // extern "C"
  43.  
  44.  

« Last Edit: October 13, 2026, 09:41:09 am by rasberryrabbit »
Code is long, Life is short, AI is not your enemy.

shortcut

  • New Member
  • *
  • Posts: 16
@rasberryrabbit: thank you for looking at the bindings.

Accessors for the QInputMethodEvent attributes are already part of our patches. They add these C functions to qevent_c.cpp/.h, with matching externals in qt62.pas (Qt6) and qt56.pas (Qt5):

QInputMethodEvent_attributeCount, _attributeType, _attributeStart, _attributeLength, _attributeBackgroundColor, _attributeBold

https://github.com/53jouhikone-source/JPSupport/tree/main/JPSupport-Qt/patches/upstream

They are separate small functions instead of one struct. Compared with your sketch, they do not return the underline flag or the foreground color; they return the background color and bold instead. The LCL side maps the attributes to preedit segments with a new LM_IM_SET_PREEDIT message, so the clause being converted is highlighted in the editor.

I have only tested Japanese (fcitx5 + Mozc). If you can try it with another IME, for example Korean or Chinese, I would be glad to hear what works and what does not. If you need the underline or foreground color for your case, tell me, and I will look at adding them.

Best regards,
Shortcut

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12760
  • Debugger - SynEdit - and more
    • wiki
How close are those attributes in https://learn.microsoft.com/en-us/windows/win32/intl/composition-string
(read via ImmGetCompositionStringW when triggered by to GCS_COMPATTR in  WM_IME_COMPOSITION)




rasberryrabbit

  • Full Member
  • ***
  • Posts: 158
@rasberryrabbit: thank you for looking at the bindings.

Accessors for the QInputMethodEvent attributes are already part of our patches. They add these C functions to qevent_c.cpp/.h, with matching externals in qt62.pas (Qt6) and qt56.pas (Qt5):

QInputMethodEvent_attributeCount, _attributeType, _attributeStart, _attributeLength, _attributeBackgroundColor, _attributeBold

https://github.com/53jouhikone-source/JPSupport/tree/main/JPSupport-Qt/patches/upstream

They are separate small functions instead of one struct. Compared with your sketch, they do not return the underline flag or the foreground color; they return the background color and bold instead. The LCL side maps the attributes to preedit segments with a new LM_IM_SET_PREEDIT message, so the clause being converted is highlighted in the editor.

I have only tested Japanese (fcitx5 + Mozc). If you can try it with another IME, for example Korean or Chinese, I would be glad to hear what works and what does not. If you need the underline or foreground color for your case, tell me, and I will look at adding them.

Best regards,
Shortcut

It appears to be more reliable in responding to Japanese Input Method.

After applying the patch, there appears to be no compatibility issues with previous versions. I believe that once these patches are applied, there will be a better input method in QT. There were a lot of things I missed in the QT support patch in the current trunk because I don't know anything about Japanese input.
« Last Edit: October 13, 2026, 04:01:08 pm by rasberryrabbit »
Code is long, Life is short, AI is not your enemy.

rasberryrabbit

  • Full Member
  • ***
  • Posts: 158
How close are those attributes in https://learn.microsoft.com/en-us/windows/win32/intl/composition-string
(read via ImmGetCompositionStringW when triggered by to GCS_COMPATTR in  WM_IME_COMPOSITION)

Pango, MS IME, and QT have different attribute sets and are not compatible.
Code is long, Life is short, AI is not your enemy.

 

TinyPortal © 2005-2018