Java: Liste de candidats égarés lors de l'utilisation du chinois (traditionnel) - Nouveau clavier phonétique


J'essaie d'utiliser le Chinois (Traditionnel, Taiwan), Chinois (Traditionnel) - Nouveau clavier phonétique sur un anglais (US) Windows 7. Lorsque je tape dans une zone de texte basée sur Java Swing, la liste des candidats s'affiche en bas à droite de mon écran, quel que soit l'endroit où la zone de texte est positionnée sur l'écran. Lorsque je n'utilise pas de programme Java, la liste des candidats apparaît au bon endroit, directement sous le texte que je tape.

Quelqu'un d'autre A rencontré ce comportement et vous avez trouvé une solution de contournement? Je n'ai pas trouvé d'autres rapports de ce comportement en ligne.

Merci d'avance pour toute aide!

Les Détails Du Système:

  • Microsoft Nouvel IME phonétique 10.1 (10.1.7601.0)
    • Mode d'entrée chinois
    • Demi ou pleine forme (peu importe)
    • Disposition standard du clavier
  • Windows 7, 64 bits (même chose sur 32 bits)
  • Affecte Java 6, 7 et 8
  • Affecte le Swing et JavaFX
Author: Patrick C, 2015-02-13

1 answers

J'ai finalement trouvé des problèmes similaires signalés, mais la plupart d'entre eux étaient liés aux IME japonais et ont déjà été corrigés dans le JDK. Je n'ai trouvé aucun rapport spécifique à cet IME chinois, mais j'ai trouvé une solution de contournement au cas où elle serait utile pour les autres.

Le bref résumé est que j'écoute le message Windows WM_IME_STARTCOMPOSITION. Quand je vois cela, je localise la fenêtre candidate IME, la déplace à l'emplacement que je veux et remplace son WindowProc pour empêcher d'autres mouvements. Lors composition J'écoute également les événements WM_KEYDOWN car je n'ai plus reçu de messages WM_IME pendant que l'utilisateur composait, même si la fenêtre candidate se ferme et est recréée plusieurs fois tout au long de la composition. Lorsque je reçois le message WM_IME_ENDCOMPOSITON, j'arrête d'écouter les messages WM_KEYDOWN.

Comme approche alternative, j'ai essayé d'envoyer un message WM_IME_CONTROL avec la commande IMC_SETCANDIDATEPOS pour déplacer la fenêtre candidate, mais cet IME particulier semble ignorer.

J'ai utilisé JNA (https://github.com/twall/jna) pour remplacer le WindowProc à la fois sur la fenêtre contenant ma zone de texte ainsi que sur la fenêtre candidate IME.

L'extrait de code ci-dessous est un exemple de solution de contournement.

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);

Le code ci-dessus suppose les définitions suivantes:

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);
}
 0
Author: Patrick C, 2015-04-14 16:18:58