Java:使用中文(繁体)时错位的候选列表 - 新注音键盘

Java: Misplaced candidate list when using Chinese (Traditional) - New Phonetic keyboard

我正在尝试在英语(美国)上使用中文(繁体,台湾)、中文(繁体)- 新注音键盘 Windows 7. 当我输入 Java基于 Swing 的文本区域,候选列表显示在我屏幕的右下角,无论文本区域在屏幕上的位置如何。当我不使用 Java 程序时,候选列表会显示在正确的位置,就在我输入的文本下方。

有其他人 运行 了解此行为并找到解决方法吗?我还没有在网上找到关于此行为的其他报告。

在此先感谢您的帮助!

系统详情:

我最终确实发现了类似的问题,但其中大部分与日语 IME 相关,并且已在 JDK 中修复。我没有找到任何特定于此中文输入法的报告,但我确实找到了一种解决方法,以防它对其他人有用。

简单总结就是我监听了WM_IME_STARTCOMPOSITIONWindows消息。当我看到它时,我找到 IME 候选 window,将其移动到我想要的位置,并覆盖其 WindowProc 以防止进一步移动。在组合过程中,我还监听 WM_KEYDOWN 事件,因为在用户组合时我不再收到任何 WM_IME 消息,即使候选 window 在整个组合过程中关闭并重新创建了几次。当我收到 WM_IME_ENDCOMPOSITON 消息时,我停止收听 WM_KEYDOWN 消息。

作为替代方法,我尝试使用 IMC_SETCANDIDATEPOS 命令发送 WM_IME_CONTROL 消息来移动候选 window,但这个特定的 IME 似乎忽略了它。

我使用 JNA (https://github.com/twall/jna) 在包含我的文本区域的 window 以及 IME 候选 window.

上覆盖 WindowProc

下面的代码片段是解决方法的示例。

hwndMain = WIN_INSTANCE.FindWindow(null, "Main Window");

// Note the existing WindowProc so we can restore it later.
prevWndProc = new BaseTSD.LONG_PTR((long) WIN_INSTANCE.GetWindowLong(hwndMain, WinUser.GWL_WNDPROC));

// Register a new WindowProc that we will use to intercept IME messages.
mainListener = new WindowsCallbackListener() {
    @Override
    public int callback(int hWnd, int uMsg, int uParam, int lParam) {
        if (uMsg == WM_IME_STARTCOMPOSITION || (imeComposing && uMsg == WM_KEYDOWN)) {
            imeComposing = true;

            final WinDef.HWND hwndIme = WIN_INSTANCE.FindWindow("SYSIME7_READING_UI", null);

            if (hwndIme != null && !hwndIme.equals(imeWindow)) {
                // We found an IME window that is not the same as the last one. We assume the last one was
                // closed. We need to register our callback with the new window.
                imeWindow = hwndIme;

                final Point imeWindowLocation = getImeWindowLocation();
                WIN_INSTANCE.MoveWindow(hwndIme, imeWindowLocation.x, imeWindowLocation.y, 0, 0, true);

                final BaseTSD.LONG_PTR prevWndProcIme =
                        new BaseTSD.LONG_PTR((long) WIN_INSTANCE.GetWindowLong(hwndIme, WinUser.GWL_WNDPROC));

                imeListener = new WindowsCallbackListener() {
                    @Override
                    public int callback(int hWnd, int uMsg, int uParam, int lParam) {
                        if (uMsg == WM_WINDOWPOSCHANGING) {
                            final WindowPosition pos = new WindowPosition(new Pointer((long)lParam));
                            pos.read();
                            pos.flags |= SWP_NOMOVE;
                            pos.write();
                        }

                        // Call the window's actual WndProc so the events get processed.
                        return WIN_INSTANCE.CallWindowProc(prevWndProcIme, hWnd, uMsg, uParam, lParam);
                    }
                };

                // Set the WndProc function to use our callback listener instead of the window's one.
                WIN_INSTANCE.SetWindowLong(hwndIme, WinUser.GWL_WNDPROC, imeListener);
            }
        }
        else if (uMsg == WM_IME_ENDCOMPOSITION) {
            // We can discard the IME listener since its window is closed. If another one gets opened, we'll
            // create a new listener.
            imeListener = null;
            imeComposing = false;
        }

        // Call the window's previous WindowProc so the event continues to get processed.
        return WIN_INSTANCE.CallWindowProc(prevWndProc, hWnd, uMsg, uParam, lParam);
    }
};

// Set the WindowProc function to use our WindowProc so the event continues to get processed.
WIN_INSTANCE.SetWindowLong(hwndMain, WinUser.GWL_WNDPROC, mainListener);

以上代码采用以下定义:

private static final MyUser32 WIN_INSTANCE = MyUser32.INSTANCE;
private static final int SWP_NOMOVE = 2;
private static final int WM_KEYDOWN = 256;
private static final int WM_WINDOWPOSCHANGING = 70;
private static final int WM_IME_ENDCOMPOSITION = 270;
private static final int WM_IME_STARTCOMPOSITION = 269;
private WinDef.HWND hwndMain;
private BaseTSD.LONG_PTR prevWndProc;

// Keep references to these listeners so they don't get garbage-collected.
private WindowsCallbackListener mainListener;
private WindowsCallbackListener imeListener;

private boolean imeComposing;
private WinDef.HWND imeWindow;

public static class WindowPosition extends Structure {
    public WinDef.HWND hwnd;
    public WinDef.HWND hwndInsertAfter;
    public int x;
    public int y;
    public int cx;
    public int cy;
    public int flags;

    public WindowPosition(Pointer p) {
        super(p);
    }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList("hwnd", "hwndInsertAfter", "x", "y", "cx", "cy", "flags");
    }
}

private interface MyUser32 extends User32 {
    MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);
    int CallWindowProc(BaseTSD.LONG_PTR prevWndProc, int hWnd, int uMsg, int uParam, int lParam);
    int SetWindowLong(HWND hwnd, int nIndex, BaseTSD.LONG_PTR listener) throws LastErrorException;
    int SetWindowLong(HWND hwnd, int nIndex, WindowsCallbackListener listener) throws LastErrorException;
}

private interface WindowsCallbackListener extends Callback, StdCall {
    int callback(int hWnd, int Msg, int wParam, int lParam);
}