-
Bug
-
Resolution: Fixed
-
P4
-
jfx11.0.12, jfx17
-
b18
-
generic
-
windows
ADDITIONAL SYSTEM INFORMATION :
Windows 10
JavaFX 17
OpenJDK 11.0.11
A DESCRIPTION OF THE PROBLEM :
KeyCharacterCombinations for symbols like '+', '\' and '-' can fail on non-US keyboards. The Windows implementation of the Toolkit call getKeyCodeForChar uses a fixed table which is only valid for US QWERTY. This causes a mismatch between the way getKeyCodeForChar works and the way key codes are assigned when a KeyEvent is generated.
The test application constructs a KeyCharacterCombination for each KeyTyped event and verifies that the combination matches the KeyPressed event that generated the character.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached test.
Switch to a German keyboard layout.
Press the German '+' key (where right bracket is on a US keyboard).
Press the German '#' key (where back slash is on a US keyboard).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The log area should show these messages:
Passed: key code Plus matched +
Passed: key code Number Sign matched #
ACTUAL -
The log area shows these messages:
* Failed: key code Plus did not match +
* Failed: key code Number Sign did not match #
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyCharacterCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CharComboTest extends Application {
private final TextArea typingArea = new TextArea("");
private KeyEvent lastPressed = null;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
typingArea.setEditable(false);
typingArea.appendText("Shifted punctuation keys are most likely to fail.\nKeys that don't generate text will be ignored.\n\n");
typingArea.addEventFilter(KeyEvent.KEY_PRESSED, this::pressedEvent);
typingArea.addEventFilter(KeyEvent.KEY_RELEASED, this::releasedEvent);
typingArea.addEventFilter(KeyEvent.KEY_TYPED, this::typedEvent);
Scene scene = new Scene(typingArea, 640, 640);
stage.setScene(scene);
stage.setTitle("Key Character Combinations");
stage.show();
Platform.runLater(typingArea::requestFocus);
}
// Helper Methods for Event Handling
private void passed(String str) {
typingArea.appendText("Passed: " + str + "\n");
}
private void failed(String str) {
typingArea.appendText("* Failed: " + str + "\n");
}
private void ignored(String str) {
typingArea.appendText("Ignored: " + str + "\n");
}
private void pressedEvent(KeyEvent e) {
lastPressed = e;
}
private void releasedEvent(KeyEvent e) {
lastPressed = null;
}
private KeyCombination.ModifierValue boolToModifier(boolean down)
{
if (down)
return KeyCombination.ModifierValue.DOWN;
return KeyCombination.ModifierValue.UP;
}
private void typedEvent(KeyEvent e) {
if (lastPressed == null)
return;
if (e.getCharacter().isEmpty())
return;
// Keys that only generate characters with diacritics are not assigned key codes.
if (lastPressed.getCode() == KeyCode.UNDEFINED)
{
ignored("undefined key code for " + e.getCharacter());
return;
}
// Keys on the numeric keypad will not match.
if (lastPressed.getCode().isKeypadKey()) {
ignored("keypad code " + lastPressed.getCode().getName() + " generated " + e.getCharacter());
return;
}
KeyCombination.ModifierValue shiftModifier = boolToModifier(lastPressed.isShiftDown());
KeyCombination.ModifierValue controlModifier = boolToModifier(lastPressed.isControlDown());
KeyCombination.ModifierValue altModifier = boolToModifier(lastPressed.isAltDown());
KeyCombination.ModifierValue metaModifier = boolToModifier(lastPressed.isMetaDown());
KeyCombination.ModifierValue shortcutModifier = boolToModifier(lastPressed.isShortcutDown());
KeyCharacterCombination combination = new KeyCharacterCombination(e.getCharacter(),
shiftModifier, controlModifier, altModifier, metaModifier, shortcutModifier);
String combinationDescription = combination.getDisplayText();
if (lastPressed.getCode().isWhitespaceKey())
{
if (!combinationDescription.isEmpty())
combinationDescription = combinationDescription.substring(0, combinationDescription.length() - 1);
combinationDescription += lastPressed.getCode().getName();
}
if (combination.match(lastPressed))
passed("key code " + lastPressed.getCode().getName() + " matched " + combinationDescription);
else
failed("key code " + lastPressed.getCode().getName() + " did not match " + combinationDescription);
}
}
---------- END SOURCE ----------
FREQUENCY : always
Windows 10
JavaFX 17
OpenJDK 11.0.11
A DESCRIPTION OF THE PROBLEM :
KeyCharacterCombinations for symbols like '+', '\' and '-' can fail on non-US keyboards. The Windows implementation of the Toolkit call getKeyCodeForChar uses a fixed table which is only valid for US QWERTY. This causes a mismatch between the way getKeyCodeForChar works and the way key codes are assigned when a KeyEvent is generated.
The test application constructs a KeyCharacterCombination for each KeyTyped event and verifies that the combination matches the KeyPressed event that generated the character.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached test.
Switch to a German keyboard layout.
Press the German '+' key (where right bracket is on a US keyboard).
Press the German '#' key (where back slash is on a US keyboard).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The log area should show these messages:
Passed: key code Plus matched +
Passed: key code Number Sign matched #
ACTUAL -
The log area shows these messages:
* Failed: key code Plus did not match +
* Failed: key code Number Sign did not match #
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyCharacterCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CharComboTest extends Application {
private final TextArea typingArea = new TextArea("");
private KeyEvent lastPressed = null;
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
typingArea.setEditable(false);
typingArea.appendText("Shifted punctuation keys are most likely to fail.\nKeys that don't generate text will be ignored.\n\n");
typingArea.addEventFilter(KeyEvent.KEY_PRESSED, this::pressedEvent);
typingArea.addEventFilter(KeyEvent.KEY_RELEASED, this::releasedEvent);
typingArea.addEventFilter(KeyEvent.KEY_TYPED, this::typedEvent);
Scene scene = new Scene(typingArea, 640, 640);
stage.setScene(scene);
stage.setTitle("Key Character Combinations");
stage.show();
Platform.runLater(typingArea::requestFocus);
}
// Helper Methods for Event Handling
private void passed(String str) {
typingArea.appendText("Passed: " + str + "\n");
}
private void failed(String str) {
typingArea.appendText("* Failed: " + str + "\n");
}
private void ignored(String str) {
typingArea.appendText("Ignored: " + str + "\n");
}
private void pressedEvent(KeyEvent e) {
lastPressed = e;
}
private void releasedEvent(KeyEvent e) {
lastPressed = null;
}
private KeyCombination.ModifierValue boolToModifier(boolean down)
{
if (down)
return KeyCombination.ModifierValue.DOWN;
return KeyCombination.ModifierValue.UP;
}
private void typedEvent(KeyEvent e) {
if (lastPressed == null)
return;
if (e.getCharacter().isEmpty())
return;
// Keys that only generate characters with diacritics are not assigned key codes.
if (lastPressed.getCode() == KeyCode.UNDEFINED)
{
ignored("undefined key code for " + e.getCharacter());
return;
}
// Keys on the numeric keypad will not match.
if (lastPressed.getCode().isKeypadKey()) {
ignored("keypad code " + lastPressed.getCode().getName() + " generated " + e.getCharacter());
return;
}
KeyCombination.ModifierValue shiftModifier = boolToModifier(lastPressed.isShiftDown());
KeyCombination.ModifierValue controlModifier = boolToModifier(lastPressed.isControlDown());
KeyCombination.ModifierValue altModifier = boolToModifier(lastPressed.isAltDown());
KeyCombination.ModifierValue metaModifier = boolToModifier(lastPressed.isMetaDown());
KeyCombination.ModifierValue shortcutModifier = boolToModifier(lastPressed.isShortcutDown());
KeyCharacterCombination combination = new KeyCharacterCombination(e.getCharacter(),
shiftModifier, controlModifier, altModifier, metaModifier, shortcutModifier);
String combinationDescription = combination.getDisplayText();
if (lastPressed.getCode().isWhitespaceKey())
{
if (!combinationDescription.isEmpty())
combinationDescription = combinationDescription.substring(0, combinationDescription.length() - 1);
combinationDescription += lastPressed.getCode().getName();
}
if (combination.match(lastPressed))
passed("key code " + lastPressed.getCode().getName() + " matched " + combinationDescription);
else
failed("key code " + lastPressed.getCode().getName() + " did not match " + combinationDescription);
}
}
---------- END SOURCE ----------
FREQUENCY : always