この記事では IME関連のバグの一つ[JDK-8189282] の回避方法を紹介します。
ここで紹介するコードは一時的な解決です。将来の JavaFX のバージョンでは動作しないかもしれないので注意してください。 また OpenJFX でこのバグが修正された際には、この回避コードは除外する必要があります。 |
問題
「JavaFX: Invalid position of candidate pop-up of InputMethod in Hi-DPI on Windows」 https://bugs.openjdk.java.net/browse/JDK-8189282
この問題は私が2017年に報告していたもので、報告後に別のより良いワークアラウンドを見つけました。 IM (Input method)を使用する言語圏では、JavaFX 技術の採否を分けるレベルの重要度があると考えられます。
環境
この問題は次の環境で発生しています。
-
Windows 10
-
HIDPI
-
JDK 8u60, 9, 10
8u60以降では、スケールが150% 以上になる場合に、HIDPI モードがデフォルトで有効になることで、 この問題は顕在化します。
解決
私はこの問題では2つの回避方法を見つけています。
消極的な解決
最初に思いつく方法は HIDPI モードをオフにする方法で簡単ですが、 進歩的ではありません。
-Dprism.allowhidpi=false
積極的な解決
バグの報告後に見つけた回避方法を説明します。
放置されていますが、問題の回避はそれほど難しくはありません。
テキスト入力をサポートするコントロールでは、 InputMethodRequests インタフェースでIME の表示位置を制御できますので、 正しいスケールで補正することで問題を回避できます。
if(SystemUtils.IS_OS_WINDOWS) {
final InputMethodRequests imr = textControl.getInputMethodRequests();
textControl.setInputMethodRequests(new WindowsInputMethodRequests(imr));
}
/**
* @author Naohiro Yoshimoto (yosbits.com)
*/
public final class WindowsInputMethodRequests implements ExtendedInputMethodRequests {
private final InputMethodRequests imr;
public WindowsInputMethodRequests(final InputMethodRequests imr) {
this.imr = imr;
}
private static double getUIScale() {
try {
Method method=null;
final Screen mainScreen = Screen.getMainScreen();
final Class<? extends Screen> mainScreenClass = mainScreen.getClass();
try {
method = mainScreenClass.getDeclaredMethod("getPlatformScaleX");
} catch (NoSuchMethodException e) {
try {
method = mainScreenClass.getDeclaredMethod("getUIScale");
} catch (NoSuchMethodException e1) {
//do nothing
}
}
if(method==null) {
return 1;
}
return ((Number)method.invoke(mainScreen)).doubleValue();
}catch(Exception e) {
e.printStackTrace();
}
return 1;
}
@Override
public Point2D getTextLocation(int offset) {
double scale = getUIScale();
return imr.getTextLocation(offset).multiply(scale);
}
@Override
public int getLocationOffset(int x, int y) {
double scale = getUIScale();
return (int)(imr.getLocationOffset(x,y)*scale);
}
@Override
public void cancelLatestCommittedText() {
imr.cancelLatestCommittedText();
}
@Override
public String getSelectedText() {
System.out.println(imr.getSelectedText());
return imr.getSelectedText();
}
@Override
public int getInsertPositionOffset() {
double scale = getUIScale();
return (int)(((ExtendedInputMethodRequests)imr).getInsertPositionOffset() * scale);
}
//TODO: It has not been confirmed whether this method is used.
@Override
public String getCommittedText(int begin, int end) {
System.out.println("getCommittedText");
String text = ((ExtendedInputMethodRequests)imr).getCommittedText(begin, end);
System.out.println(text);
return text;
}
//TODO: It has not been confirmed whether this method is used.
@Override
public int getCommittedTextLength() {
System.out.println("getCommittedTextLength");
return ((ExtendedInputMethodRequests)imr).getCommittedTextLength();
}
}
このワークアラウンドはJDK 8, 9以降での動作を確認しています。 このコードを LnF(Skin) で実装すれば、この回避方法を意識する必要はなくなります。
YOSBITS では JavaFX のコンサルティングを行なっていますので、 他に質問があればメールで承ります。 コンタクトを参照してください。