Java:使用中文(繁体)时错位的候选列表 - 新注音键盘
Java: Misplaced candidate list when using Chinese (Traditional) - New Phonetic keyboard
我正在尝试在英语(美国)上使用中文(繁体,台湾)、中文(繁体)- 新注音键盘 Windows 7. 当我输入 Java基于 Swing 的文本区域,候选列表显示在我屏幕的右下角,无论文本区域在屏幕上的位置如何。当我不使用 Java 程序时,候选列表会显示在正确的位置,就在我输入的文本下方。
有其他人 运行 了解此行为并找到解决方法吗?我还没有在网上找到关于此行为的其他报告。
在此先感谢您的帮助!
系统详情:
- Microsoft 新语音输入法 10.1 (10.1.7601.0)
- 中文输入模式
- 半形或全形(无所谓)
- 标准键盘布局
- Windows 7、64 位(32 位也是如此)
- 影响 Java 6、7 和 8
- 影响摇摆和 JavaFX
我最终确实发现了类似的问题,但其中大部分与日语 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);
}
我正在尝试在英语(美国)上使用中文(繁体,台湾)、中文(繁体)- 新注音键盘 Windows 7. 当我输入 Java基于 Swing 的文本区域,候选列表显示在我屏幕的右下角,无论文本区域在屏幕上的位置如何。当我不使用 Java 程序时,候选列表会显示在正确的位置,就在我输入的文本下方。
有其他人 运行 了解此行为并找到解决方法吗?我还没有在网上找到关于此行为的其他报告。
在此先感谢您的帮助!
系统详情:
- Microsoft 新语音输入法 10.1 (10.1.7601.0)
- 中文输入模式
- 半形或全形(无所谓)
- 标准键盘布局
- Windows 7、64 位(32 位也是如此)
- 影响 Java 6、7 和 8
- 影响摇摆和 JavaFX
我最终确实发现了类似的问题,但其中大部分与日语 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);
}