mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-2460: Wrong position of input window and no input preview with fcitx and ubuntu 13.04.
- introduces and integrates jbNewXimClient: a new implementation of XIC creation routine (it's mostly refactoring and generalizing of AWT's existed code). Enabled by default and can be disabled via a new system property -Djb.awt.newXimClient.enabled=false ; - introduces support of the X11's native over-the-spot input method style (it's almost the same as AWT's below-the-spot mode, but the input method's windows are drawn externally, not by AWT). Enabled by default and can be disabled via a new system property -Djb.awt.newXimClient.enabled=false. Doesn't work if -Djb.awt.newXimClient.enabled=false is set ; - introduces sun.awt.X11.XInputMethod.ClientComponentCaretPositionTracker class that tracks all kind of events for the current client component that can lead to the caret position changing ; - makes the XInputMethod class to update the input window's position (whenever the ClientComponentCaretPositionTracker discovers that's necessary) by setting the X11's XNSpotLocation property . Check out the branch nprovotorov/backups/JBR-2460_wrong-position-of-input-window-and-no-input-preview for more granular patches. (cherry picked from commitc57030a2ef) (cherry picked from commit56d9759667)
This commit is contained in:
committed by
jbrbot
parent
df42c9c033
commit
d8f8249646
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -25,18 +26,29 @@
|
||||
|
||||
package sun.awt.X11;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.im.InputMethodRequests;
|
||||
import java.awt.im.spi.InputMethodContext;
|
||||
import java.awt.peer.ComponentPeer;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.X11GraphicsDevice;
|
||||
import sun.awt.X11InputMethod;
|
||||
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
/**
|
||||
* Input Method Adapter for XIM (without Motif)
|
||||
*
|
||||
@@ -47,6 +59,7 @@ public class XInputMethod extends X11InputMethod {
|
||||
|
||||
public XInputMethod() throws AWTException {
|
||||
super();
|
||||
clientComponentCaretPositionTracker = new ClientComponentCaretPositionTracker(this);
|
||||
}
|
||||
|
||||
public void setInputMethodContext(InputMethodContext context) {
|
||||
@@ -58,8 +71,69 @@ public class XInputMethod extends X11InputMethod {
|
||||
if (peer != null) {
|
||||
adjustStatusWindow(peer.getContentWindow());
|
||||
}
|
||||
|
||||
if (doesSupportMovingCandidatesNativeWindow) {
|
||||
clientComponentCaretPositionTracker.onNotifyClientWindowChange(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void activate() {
|
||||
super.activate();
|
||||
|
||||
if (doesSupportMovingCandidatesNativeWindow) {
|
||||
updateCandidatesNativeWindowPosition(true);
|
||||
clientComponentCaretPositionTracker.startTracking(getClientComponent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void deactivate(boolean isTemporary) {
|
||||
clientComponentCaretPositionTracker.stopTrackingCurrentComponent();
|
||||
super.deactivate(isTemporary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(AWTEvent e) {
|
||||
if (doesSupportMovingCandidatesNativeWindow) {
|
||||
clientComponentCaretPositionTracker.onDispatchEvent(e);
|
||||
}
|
||||
super.dispatchEvent(e);
|
||||
}
|
||||
|
||||
|
||||
// Is called from native
|
||||
private static boolean isJbNewXimClientEnabled() {
|
||||
try {
|
||||
final String strVal = System.getProperty("jb.awt.newXimClient.enabled");
|
||||
final boolean defVal = true;
|
||||
|
||||
return (strVal == null) ? defVal : Boolean.parseBoolean(strVal);
|
||||
} catch (Exception err) {
|
||||
if (log.isLoggable(PlatformLogger.Level.SEVERE)) {
|
||||
log.severe("Error at isJbNewXimClientEnabled", err);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean preferXBelowTheSpot() {
|
||||
try {
|
||||
final String strVal = System.getProperty("jb.awt.newXimClient.preferBelowTheSpot");
|
||||
final boolean defVal = true;
|
||||
|
||||
return (strVal == null) ? defVal : Boolean.parseBoolean(strVal);
|
||||
} catch (Exception err) {
|
||||
if (log.isLoggable(PlatformLogger.Level.SEVERE)) {
|
||||
log.severe("Error at isJbNewXimClientEnabled", err);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected boolean openXIM() {
|
||||
return openXIMNative(XToolkit.getDisplay());
|
||||
}
|
||||
@@ -69,14 +143,14 @@ public class XInputMethod extends X11InputMethod {
|
||||
if (peer == null) {
|
||||
return false;
|
||||
}
|
||||
return createXICNative(peer.getContentWindow());
|
||||
return createXICNative(peer.getContentWindow(), preferXBelowTheSpot());
|
||||
}
|
||||
|
||||
protected boolean recreateXIC(int ctxid) {
|
||||
final XComponentPeer peer = (XComponentPeer)getPeer(clientComponentWindow);
|
||||
if (peer == null || pData == 0)
|
||||
return true;
|
||||
return recreateXICNative(peer.getContentWindow(), pData, ctxid);
|
||||
return recreateXICNative(peer.getContentWindow(), pData, ctxid, preferXBelowTheSpot());
|
||||
}
|
||||
protected int releaseXIC() {
|
||||
if (pData == 0)
|
||||
@@ -86,8 +160,7 @@ public class XInputMethod extends X11InputMethod {
|
||||
|
||||
private static volatile long xicFocus;
|
||||
|
||||
protected void setXICFocus(ComponentPeer peer,
|
||||
boolean value, boolean active) {
|
||||
protected void setXICFocus(ComponentPeer peer, boolean value, boolean active) {
|
||||
if (peer == null) {
|
||||
return;
|
||||
}
|
||||
@@ -95,15 +168,17 @@ public class XInputMethod extends X11InputMethod {
|
||||
setXICFocusNative(((XComponentPeer)peer).getContentWindow(),
|
||||
value,
|
||||
active);
|
||||
|
||||
doesSupportMovingCandidatesNativeWindow = value && doesFocusedXICSupportMovingCandidatesNativeWindow();
|
||||
}
|
||||
|
||||
public static long getXICFocus() {
|
||||
return xicFocus;
|
||||
}
|
||||
|
||||
/* XAWT_HACK FIX ME!
|
||||
do NOT call client code!
|
||||
*/
|
||||
/* XAWT_HACK FIX ME!
|
||||
do NOT call client code!
|
||||
*/
|
||||
protected Container getParent(Component client) {
|
||||
return client.getParent();
|
||||
}
|
||||
@@ -245,14 +320,397 @@ public class XInputMethod extends X11InputMethod {
|
||||
}
|
||||
|
||||
|
||||
// JBR-2460
|
||||
private volatile boolean doesSupportMovingCandidatesNativeWindow = false;
|
||||
private Point lastKnownCandidatesNativeWindowAbsolutePosition = null;
|
||||
|
||||
private void updateCandidatesNativeWindowPosition(final boolean forceUpdate) {
|
||||
assert(SwingUtilities.isEventDispatchThread());
|
||||
|
||||
if (!doesSupportMovingCandidatesNativeWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Component clientComponent = getClientComponent();
|
||||
if (clientComponent == null) {
|
||||
// No client
|
||||
return;
|
||||
}
|
||||
|
||||
final Window clientComponentWindow = getClientComponentWindow();
|
||||
if (clientComponentWindow == null) {
|
||||
// Impossible?
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clientComponent.isShowing() || (!clientComponentWindow.isShowing())) {
|
||||
// Components are not showing yet, so it's impossible to determine their location on the screen
|
||||
// and/or the location of the caret
|
||||
return;
|
||||
}
|
||||
|
||||
final Point clientComponentAbsolutePos = clientComponent.getLocationOnScreen();
|
||||
final int clientComponentAbsoluteMaxX = clientComponentAbsolutePos.x + clientComponent.getWidth();
|
||||
final int clientComponentAbsoluteMaxY = clientComponentAbsolutePos.y + clientComponent.getHeight();
|
||||
|
||||
// Initial values are the fallback which is the bottom-left corner of the component
|
||||
final Point expectedCandidatesNativeWindowAbsolutePos = new Point(
|
||||
clientComponentAbsolutePos.x,
|
||||
clientComponentAbsoluteMaxY
|
||||
);
|
||||
|
||||
final InputMethodRequests clientImr = clientComponent.getInputMethodRequests();
|
||||
if (clientImr != null) {
|
||||
// An active client
|
||||
|
||||
final Rectangle caretRect = clientImr.getTextLocation(null);
|
||||
if (caretRect != null) {
|
||||
expectedCandidatesNativeWindowAbsolutePos.x = caretRect.x;
|
||||
expectedCandidatesNativeWindowAbsolutePos.y = caretRect.y + caretRect.height;
|
||||
}
|
||||
}
|
||||
|
||||
// Clamping within the client component's visible rect (if available and not empty) or just its bounds
|
||||
final var clientComponentVisibleRect = getJComponentVisibleRectIfNotEmpty(clientComponent);
|
||||
if (clientComponentVisibleRect == null) {
|
||||
expectedCandidatesNativeWindowAbsolutePos.x =
|
||||
Math.max(clientComponentAbsolutePos.x, Math.min(expectedCandidatesNativeWindowAbsolutePos.x, clientComponentAbsoluteMaxX - 1));
|
||||
expectedCandidatesNativeWindowAbsolutePos.y =
|
||||
Math.max(clientComponentAbsolutePos.y, Math.min(expectedCandidatesNativeWindowAbsolutePos.y, clientComponentAbsoluteMaxY - 1));
|
||||
} else {
|
||||
final int visibleBoundsAbsoluteMinX = clientComponentAbsolutePos.x + clientComponentVisibleRect.x;
|
||||
final int visibleBoundsAbsoluteMaxX = visibleBoundsAbsoluteMinX + clientComponentVisibleRect.width;
|
||||
final int visibleBoundsAbsoluteMinY = clientComponentAbsolutePos.y + clientComponentVisibleRect.y;
|
||||
final int visibleBoundsAbsoluteMaxY = visibleBoundsAbsoluteMinY + clientComponentVisibleRect.height;
|
||||
|
||||
expectedCandidatesNativeWindowAbsolutePos.x =
|
||||
Math.max(visibleBoundsAbsoluteMinX, Math.min(expectedCandidatesNativeWindowAbsolutePos.x, visibleBoundsAbsoluteMaxX - 1));
|
||||
expectedCandidatesNativeWindowAbsolutePos.y =
|
||||
Math.max(visibleBoundsAbsoluteMinY, Math.min(expectedCandidatesNativeWindowAbsolutePos.y, visibleBoundsAbsoluteMaxY - 1));
|
||||
}
|
||||
|
||||
// Scaling the coordinates according to the screen's current scaling settings.
|
||||
// To do it properly, we have to know the screen which the point is on.
|
||||
// The code below supposes this is the one which clientComponent belongs to, because we've clamped
|
||||
// the point coordinates within the component's bounds above.
|
||||
final X11GraphicsDevice candidatesNativeWindowDevice = getComponentX11Device(clientComponent);
|
||||
if (candidatesNativeWindowDevice != null) {
|
||||
expectedCandidatesNativeWindowAbsolutePos.x =
|
||||
candidatesNativeWindowDevice.scaleUpX(expectedCandidatesNativeWindowAbsolutePos.x);
|
||||
expectedCandidatesNativeWindowAbsolutePos.y =
|
||||
candidatesNativeWindowDevice.scaleUpY(expectedCandidatesNativeWindowAbsolutePos.y);
|
||||
}
|
||||
|
||||
// Clamping within screen bounds (to avoid the input candidates window to appear outside a screen).
|
||||
final Rectangle closestScreenScaledBounds = new Rectangle();
|
||||
final X11GraphicsDevice candidatesNativeWindowClosestScreen = findClosestScreenToPoint(
|
||||
closestScreenScaledBounds,
|
||||
expectedCandidatesNativeWindowAbsolutePos,
|
||||
candidatesNativeWindowDevice
|
||||
);
|
||||
if (candidatesNativeWindowClosestScreen != null) {
|
||||
final int screenScaledBoundsXMax = closestScreenScaledBounds.x + closestScreenScaledBounds.width - 1;
|
||||
final int screenScaledBoundsYMax = closestScreenScaledBounds.y + closestScreenScaledBounds.height - 1;
|
||||
|
||||
expectedCandidatesNativeWindowAbsolutePos.x =
|
||||
Math.max(closestScreenScaledBounds.x, Math.min(expectedCandidatesNativeWindowAbsolutePos.x, screenScaledBoundsXMax));
|
||||
expectedCandidatesNativeWindowAbsolutePos.y =
|
||||
Math.max(closestScreenScaledBounds.y, Math.min(expectedCandidatesNativeWindowAbsolutePos.y, screenScaledBoundsYMax));
|
||||
}
|
||||
|
||||
if (forceUpdate || !expectedCandidatesNativeWindowAbsolutePos.equals(lastKnownCandidatesNativeWindowAbsolutePosition)) {
|
||||
// adjustCandidatesNativeWindowPosition expects coordinates relative to the client window
|
||||
final Point clientComponentWindowAbsolutePos = clientComponentWindow.getLocationOnScreen();
|
||||
final X11GraphicsDevice clientComponentWindowDevice = getComponentX11Device(clientComponentWindow);
|
||||
if (clientComponentWindowDevice != null) {
|
||||
clientComponentWindowAbsolutePos.x =
|
||||
clientComponentWindowDevice.scaleUpX(clientComponentWindowAbsolutePos.x);
|
||||
clientComponentWindowAbsolutePos.y =
|
||||
clientComponentWindowDevice.scaleUpY(clientComponentWindowAbsolutePos.y);
|
||||
}
|
||||
|
||||
final int relativeX = expectedCandidatesNativeWindowAbsolutePos.x - clientComponentWindowAbsolutePos.x;
|
||||
final int relativeY = expectedCandidatesNativeWindowAbsolutePos.y - clientComponentWindowAbsolutePos.y;
|
||||
|
||||
awtLock();
|
||||
try {
|
||||
adjustCandidatesNativeWindowPosition(relativeX, relativeY);
|
||||
} finally {
|
||||
awtUnlock();
|
||||
}
|
||||
|
||||
lastKnownCandidatesNativeWindowAbsolutePosition = expectedCandidatesNativeWindowAbsolutePos;
|
||||
}
|
||||
}
|
||||
|
||||
private static Rectangle getJComponentVisibleRectIfNotEmpty(final Component component) {
|
||||
if (component instanceof JComponent jComponent) {
|
||||
final Rectangle result = jComponent.getVisibleRect();
|
||||
if ((result != null) && (result.width > 0) && (result.height > 0)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static X11GraphicsDevice getComponentX11Device(final Component component) {
|
||||
if (component == null) return null;
|
||||
|
||||
final var componentGc = component.getGraphicsConfiguration();
|
||||
if (componentGc == null) return null;
|
||||
|
||||
return (componentGc.getDevice() instanceof X11GraphicsDevice result) ? result : null;
|
||||
}
|
||||
|
||||
private static X11GraphicsDevice findClosestScreenToPoint(
|
||||
final Rectangle outScreenScaledBounds,
|
||||
final Point absolutePointScaled,
|
||||
final X11GraphicsDevice... screensToCheckFirst
|
||||
) {
|
||||
assert(outScreenScaledBounds != null);
|
||||
|
||||
if (absolutePointScaled == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Iterator<X11GraphicsDevice> screensToCheck =
|
||||
Stream.concat( // screensToCheckFirst + GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()
|
||||
Arrays.stream(screensToCheckFirst),
|
||||
Stream.<Supplier<GraphicsDevice[]>>of(() -> {
|
||||
final var localGe = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
if (localGe != null) {
|
||||
return localGe.getScreenDevices();
|
||||
}
|
||||
return null;
|
||||
}).flatMap(supplier -> Stream.of(supplier.get()))
|
||||
).map(device -> (device instanceof X11GraphicsDevice screen) ? screen : null)
|
||||
.filter(Objects::nonNull)
|
||||
.iterator();
|
||||
|
||||
int closestScreenMinDistance = Integer.MAX_VALUE;
|
||||
X11GraphicsDevice result = null;
|
||||
while (screensToCheck.hasNext()) {
|
||||
final X11GraphicsDevice screen = screensToCheck.next();
|
||||
|
||||
final Rectangle screenBoundsScaled = screen.getBounds();
|
||||
if (screenBoundsScaled == null) {
|
||||
continue;
|
||||
}
|
||||
screenBoundsScaled.width = screen.scaleUp(screenBoundsScaled.width);
|
||||
screenBoundsScaled.height = screen.scaleUp(screenBoundsScaled.height);
|
||||
|
||||
final int distance = obtainDistanceBetween(screenBoundsScaled, absolutePointScaled);
|
||||
if (distance < closestScreenMinDistance) {
|
||||
result = screen;
|
||||
closestScreenMinDistance = distance;
|
||||
|
||||
outScreenScaledBounds.x = screenBoundsScaled.x;
|
||||
outScreenScaledBounds.y = screenBoundsScaled.y;
|
||||
outScreenScaledBounds.width = screenBoundsScaled.width;
|
||||
outScreenScaledBounds.height = screenBoundsScaled.height;
|
||||
|
||||
if (distance < 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int obtainDistanceBetween(final Rectangle rectangle, final Point absolutePointScaled) {
|
||||
if ((rectangle.width < 1) || (rectangle.height < 1)) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
final int screenBoundsScaledXMax = rectangle.x + rectangle.width - 1;
|
||||
final int screenBoundsScaledYMax = rectangle.y + rectangle.height - 1;
|
||||
|
||||
final int dx = Math.max(0, Math.max(rectangle.x - absolutePointScaled.x, absolutePointScaled.x - screenBoundsScaledXMax));
|
||||
final int dy = Math.max(0, Math.max(rectangle.y - absolutePointScaled.y, absolutePointScaled.y - screenBoundsScaledYMax));
|
||||
|
||||
return dx + dy; // just sum is enough for our purposes
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Native methods
|
||||
*/
|
||||
private native boolean openXIMNative(long display);
|
||||
private native boolean createXICNative(long window);
|
||||
private native boolean recreateXICNative(long window, long px11data, int ctxid);
|
||||
private native boolean createXICNative(long window, boolean preferBelowTheSpot);
|
||||
private native boolean recreateXICNative(long window, long px11data, int ctxid, boolean preferBelowTheSpot);
|
||||
private native int releaseXICNative(long px11data);
|
||||
private native void setXICFocusNative(long window,
|
||||
boolean value, boolean active);
|
||||
private native void setXICFocusNative(long window, boolean value, boolean active);
|
||||
private native void adjustStatusWindow(long window);
|
||||
|
||||
private native boolean doesFocusedXICSupportMovingCandidatesNativeWindow();
|
||||
|
||||
private native void adjustCandidatesNativeWindowPosition(int x, int y);
|
||||
|
||||
|
||||
/**
|
||||
* This class tries to track all the cases when the position of the parent XInputMethod's candidate window has
|
||||
* to be updated. Here are the examples of such cases:
|
||||
* <ul>
|
||||
* <li>The caret position has changed ;
|
||||
* <li>The component has been moved/resized ;
|
||||
* <li>The component's window has been moved/resized ;
|
||||
* <li>The component's text has been changed ;
|
||||
* </ul>
|
||||
* Tracking makes sense only when the parent XIM is in a mode allowing to move a native candidates window.
|
||||
* This is controlled by a flag {@link XInputMethod#doesSupportMovingCandidatesNativeWindow}.
|
||||
* Thus, the tracking gets enabled (via {@link #startTracking(Component)}) only when the flag is evaluated to true.
|
||||
*/
|
||||
private static class ClientComponentCaretPositionTracker implements ComponentListener, CaretListener, TextListener
|
||||
{
|
||||
public ClientComponentCaretPositionTracker(XInputMethod owner) {
|
||||
this.owner = new WeakReference<>(owner);
|
||||
}
|
||||
|
||||
|
||||
public void startTracking(final Component component) {
|
||||
stopTrackingCurrentComponent();
|
||||
|
||||
if (component == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
trackedComponent = new WeakReference<>(component);
|
||||
|
||||
// Moving and changing the size causes a possible change of caret position
|
||||
component.addComponentListener(this);
|
||||
|
||||
if (component instanceof JTextComponent jtc) {
|
||||
jtc.addCaretListener(this);
|
||||
isCaretListenerInstalled = true;
|
||||
} else if (component instanceof TextComponent tc) {
|
||||
tc.addTextListener(this);
|
||||
isTextListenerInstalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void stopTrackingCurrentComponent() {
|
||||
final Component trackedComponentStrong;
|
||||
if (trackedComponent == null) {
|
||||
trackedComponentStrong = null;
|
||||
} else {
|
||||
trackedComponentStrong = trackedComponent.get();
|
||||
trackedComponent.clear();
|
||||
trackedComponent = null;
|
||||
}
|
||||
|
||||
if (trackedComponentStrong == null) {
|
||||
isCaretListenerInstalled = false;
|
||||
isTextListenerInstalled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTextListenerInstalled) {
|
||||
isTextListenerInstalled = false;
|
||||
((TextComponent)trackedComponentStrong).removeTextListener(this);
|
||||
}
|
||||
|
||||
if (isCaretListenerInstalled) {
|
||||
isCaretListenerInstalled = false;
|
||||
((JTextComponent)trackedComponentStrong).removeCaretListener(this);
|
||||
}
|
||||
|
||||
trackedComponentStrong.removeComponentListener(this);
|
||||
}
|
||||
|
||||
/* Listening callbacks */
|
||||
|
||||
public void onDispatchEvent(AWTEvent event) {
|
||||
if (isCaretListenerInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int eventId = event.getID();
|
||||
|
||||
if ( (eventId >= MouseEvent.MOUSE_FIRST) && (eventId <= MouseEvent.MOUSE_LAST) ) {
|
||||
// The event hasn't been dispatched yet, so the caret position couldn't be changed.
|
||||
// Hence, we have to postpone the updating request.
|
||||
SwingUtilities.invokeLater(() -> updateImCandidatesNativeWindowPosition(false));
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !isTextListenerInstalled && (eventId >= KeyEvent.KEY_FIRST) && (eventId <= KeyEvent.KEY_LAST) ) {
|
||||
// The event hasn't been dispatched yet, so the caret position couldn't be changed.
|
||||
// Hence, we have to postpone the updating request.
|
||||
SwingUtilities.invokeLater(() -> updateImCandidatesNativeWindowPosition(false));
|
||||
}
|
||||
}
|
||||
|
||||
public void onNotifyClientWindowChange(Rectangle location) {
|
||||
if (location != null) {
|
||||
updateImCandidatesNativeWindowPosition(lastKnownClientWindowBounds == null);
|
||||
}
|
||||
lastKnownClientWindowBounds = location;
|
||||
}
|
||||
|
||||
// ComponentListener
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {}
|
||||
|
||||
@Override
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
updateImCandidatesNativeWindowPosition(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
updateImCandidatesNativeWindowPosition(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
updateImCandidatesNativeWindowPosition(false);
|
||||
}
|
||||
|
||||
// CaretListener
|
||||
|
||||
@Override
|
||||
public void caretUpdate(CaretEvent e) {
|
||||
updateImCandidatesNativeWindowPosition(false);
|
||||
}
|
||||
|
||||
// TextListener
|
||||
|
||||
@Override
|
||||
public void textValueChanged(TextEvent e) {
|
||||
updateImCandidatesNativeWindowPosition(false);
|
||||
}
|
||||
|
||||
/* Private parts */
|
||||
|
||||
private final WeakReference<XInputMethod> owner;
|
||||
private WeakReference<Component> trackedComponent = null;
|
||||
private boolean isCaretListenerInstalled = false;
|
||||
private boolean isTextListenerInstalled = false;
|
||||
private Rectangle lastKnownClientWindowBounds = null;
|
||||
|
||||
|
||||
private void updateImCandidatesNativeWindowPosition(boolean forceUpdate) {
|
||||
final XInputMethod ownerStrong = owner.get();
|
||||
|
||||
if ((ownerStrong == null) || (ownerStrong.isDisposed())) {
|
||||
// The owning XInputMethod instance is no longer valid
|
||||
|
||||
stopTrackingCurrentComponent();
|
||||
owner.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ownerStrong.isActive) {
|
||||
stopTrackingCurrentComponent(); // will start tracking back when the owner gets active back
|
||||
return;
|
||||
}
|
||||
|
||||
ownerStrong.updateCandidatesNativeWindowPosition(forceUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
final ClientComponentCaretPositionTracker clientComponentCaretPositionTracker;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user