zufuliu / notepad4

Notepad4 (Notepad2⨯2, Notepad2++) is a light-weight Scintilla based text editor for Windows with syntax highlighting, code folding, auto-completion and API list for many programming languages and documents, bundled with file browser plugin matepath.
Other
3.13k stars 203 forks source link

Caret position in Inline IME #210

Open maboroshin opened 4 years ago

maboroshin commented 4 years ago

I have already reported this in Scintilla's talk group. Zufuliu is also active in improving the IME's behavior.

(Default Windows notepad.exe, no inline IME)

image

(Notepad2, no inline IME)

(each 2nd images are converting the next section) image

image

(Notepad2, Red line is inline IME)

image

image

In the inline IME, the caret is at the beginning. Originally, the last seems to be correct.

(VSCode, inline IME)

image

image

(Notepad2, Green is Ideal caret position in inline IME )

83464029-a05bfc80-a4aa-11ea-9aa0-ef53ae5829fc It is the standard. The last caret in its initial state. It's the same as VSCode if caret is the last in the conversion. I think this is a good.

83465606-6f31fb00-a4af-11ea-8f1f-21c6ca00976d But it is also useful in the green position during conversion. It is not a standard.

zufuliu commented 4 years ago

What's the steps (which character typed, which key pressed) to reproduce the undesired caret position?

maboroshin commented 4 years ago

Modern Japanese hiragana order: あいうえお A I U E O かきこけこ KA KI KU KE KO さしすせそ SA SI SU SE ...continue...

Thus, I often used "A" or "I" . あああああああ = key input : "AAAAAAAA[SPACE]" いいいい = "IIII"

zufuliu commented 4 years ago

The difference between VS Code and Notepad2:

window mode
caret is always at the end of composition string

Notepad2 inline mode
put caret at the end of composition string on typing
あああああああ
           ^
move caret to the beginning of ATTR_TARGET_CONVERTED on converting, before 嗚呼
which is the caret position retrieved from GCS_CURSORPOS
嗚呼ああああ
^

VS Code inline mode
always has a caret at end of composition string
あああああああ
           ^
put another caret at the end of ATTR_TARGET_CONVERTED on converting, after 嗚呼
嗚呼ああああ
   ^     ^

Changes to log caret position and composition text attributes.

diff --git a/scintilla/win32/ScintillaWin.cxx b/scintilla/win32/ScintillaWin.cxx
index 17742138..5c552b06 100644
--- a/scintilla/win32/ScintillaWin.cxx
+++ b/scintilla/win32/ScintillaWin.cxx
@@ -1269,22 +1269,31 @@ std::vector<int> MapImeIndicators(const std::vector<BYTE> &inputStyle, int &indi
    std::vector<int> imeIndicator(attrLen, SC_INDICATOR_UNKNOWN);
    int mask = 0;

+   printf("inputStyle %d:\n", (int)attrLen);
    for (size_t i = 0; i < attrLen; i++) {
        switch (inputStyle.at(i)) {
        case ATTR_INPUT:
            imeIndicator[i] = SC_INDICATOR_INPUT;
            mask |= 1 << (SC_INDICATOR_INPUT - INDICATOR_IME);
+           printf("\tinputStyle[%d] = %s\n", (int)i, "ATTR_INPUT");
            break;
        case ATTR_TARGET_NOTCONVERTED:
+           printf("\tinputStyle[%d] = %s\n", (int)i, "ATTR_TARGET_NOTCONVERTED");
+           imeIndicator[i] = SC_INDICATOR_TARGET;
+           mask |= 1 << (SC_INDICATOR_TARGET - INDICATOR_IME);
+           break;
        case ATTR_TARGET_CONVERTED:
+           printf("\tinputStyle[%d] = %s\n", (int)i, "ATTR_TARGET_CONVERTED");
            imeIndicator[i] = SC_INDICATOR_TARGET;
            mask |= 1 << (SC_INDICATOR_TARGET - INDICATOR_IME);
            break;
        case ATTR_CONVERTED:
+           printf("\tinputStyle[%d] = %s\n", (int)i, "ATTR_CONVERTED");
            imeIndicator[i] = SC_INDICATOR_CONVERTED;
            mask |= 1 << (SC_INDICATOR_CONVERTED - INDICATOR_IME);
            break;
        default:
+           printf("\tinputStyle[%d] = %d/%s\n", (int)i, inputStyle[i], "SC_INDICATOR_UNKNOWN");
            imeIndicator[i] = SC_INDICATOR_UNKNOWN;
            mask |= 1 << (SC_INDICATOR_UNKNOWN - INDICATOR_IME);
            break;
@@ -1393,6 +1402,7 @@ sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
            // GCS_CURSORPOS value if it's available.
            Sci::Position imeEndToImeCaretU16 = -static_cast<Sci::Position>(wcs.size());
            if (!(lParam & CS_NOMOVECARET) && (lParam & GCS_CURSORPOS)) {
+               printf("\tGetImeCaretPos: %d\n", imc.GetImeCaretPos());
                imeEndToImeCaretU16 += imc.GetImeCaretPos();
            }
            if (imeEndToImeCaretU16 != 0) {
diff --git a/src/Notepad2.c b/src/Notepad2.c
index 8cb8f1f1..d181cd81 100644
--- a/src/Notepad2.c
+++ b/src/Notepad2.c
@@ -452,7 +452,7 @@ static void CleanUpResources(BOOL initialized) {
 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
-#if 0 // used for Clang UBSan or printing debug message on console.
+#if 1 // used for Clang UBSan or printing debug message on console.
    if (AttachConsole(ATTACH_PARENT_PROCESS)) {
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);

After apply above changes, build Notepad, run it from command prompt or powershell, you will get following logs. I think current implementation is OK, at least works as what the IME suggested.

1. after typing six A
inputStyle 6:
        inputStyle[0] = ATTR_INPUT
        inputStyle[1] = ATTR_INPUT
        inputStyle[2] = ATTR_INPUT
        inputStyle[3] = ATTR_INPUT
        inputStyle[4] = ATTR_INPUT
        inputStyle[5] = ATTR_INPUT
        GetImeCaretPos: 6

2. press Space
inputStyle 6:
        inputStyle[0] = ATTR_TARGET_CONVERTED
        inputStyle[1] = ATTR_TARGET_CONVERTED
        inputStyle[2] = ATTR_CONVERTED
        inputStyle[3] = ATTR_CONVERTED
        inputStyle[4] = ATTR_CONVERTED
        inputStyle[5] = ATTR_CONVERTED
        GetImeCaretPos: 0

3. press right arrow
inputStyle 6:
        inputStyle[0] = ATTR_CONVERTED
        inputStyle[1] = ATTR_CONVERTED
        inputStyle[2] = ATTR_TARGET_CONVERTED
        inputStyle[3] = ATTR_TARGET_CONVERTED
        inputStyle[4] = ATTR_CONVERTED
        inputStyle[5] = ATTR_CONVERTED
        GetImeCaretPos: 2