I'm implementing a custom virtual keyboard (my customer had specific requirements for that). Basically its made of a lot of buttons simulating keyboard events to input characters into text fields.
I found that simulating keypress/keyrelease events for characters have absolutely no effect - even though I could prove that the text field receives those events (through logs). It just doesn't result in a character appearing in the text field.
BUT: Simulating a Backspace/Tab/Arrows with the exact same mechanism works fine. Simulating a character event with a keytype event also works.
So why not use keytype events instead? Because of dead characters! I need to support letters with accent like ê (e with ^ on top). Real keyboards make you type those letters by first pressing ^ which is a dead key (no result at first) and then pressing the target letter.
If I send a keytype event for "^" and then another keytype for "e" I get "^e".
Here is a minimal code sample with 5 buttons:
public class TestApplication extends Application {
public static void main(String[] args) {
Application.launch(TestApplication.class, args);
}
@Override
public void start(Stage primaryStage) throws Exception {
TextField textField = new TextField();
textField.setOnKeyPressed(e -> {
log.info("Keypress Event: " + e.getCode());
});
textField.setOnKeyReleased(e -> {
log.info("KeyRelease Event: " + e.getCode());
});
textField.setOnKeyTyped(e -> {
log.info("KeyType Event: " + e.getCharacter());
});
// Doesn't work
Button keypressLetterButton = new Button();
keypressLetterButton.setText("e (keypress)");
keypressLetterButton.setOnMouseClicked(e -> {
pressKeyCode(KeyCode.E, textField);
});
Button keypressCircumflexButton = new Button();
keypressCircumflexButton.setText("^ (keypress)");
keypressCircumflexButton.setOnMouseClicked(e -> {
pressKeyCode(KeyCode.DEAD_CIRCUMFLEX, textField);
});
// Works
Button keypressBackspaceButton = new Button();
keypressBackspaceButton.setText("Backspace");
keypressBackspaceButton.setOnMouseClicked(e -> {
pressKeyCode(KeyCode.BACK_SPACE, textField);
});
Button keytypeLetterButton = new Button();
keytypeLetterButton.setText("e (keytype)");
keytypeLetterButton.setOnMouseClicked(e -> {
typeKey("e", textField);
});
// Works, but not as intended
Button keytypeCircumflexButton = new Button();
keytypeCircumflexButton.setText("^ (keytype)");
keytypeCircumflexButton.setOnMouseClicked(e -> {
typeKey("^", textField);
});
// Just to prevent stealing focus
keypressLetterButton.setFocusTraversable(false);
keypressCircumflexButton.setFocusTraversable(false);
keypressBackspaceButton.setFocusTraversable(false);
keytypeLetterButton.setFocusTraversable(false);
keytypeCircumflexButton.setFocusTraversable(false);
HBox hBox = new HBox(keypressLetterButton, keypressCircumflexButton, keypressBackspaceButton,
keytypeLetterButton, keytypeCircumflexButton, textField);
primaryStage.setScene(new Scene(hBox));
primaryStage.show();
}
public void pressKeyCode(KeyCode keyCode, Node target) {
KeyEvent pressEvent = new KeyEvent(KeyEvent.KEY_PRESSED, null, keyCode.getName(), keyCode, false, false, false,
false);
KeyEvent releaseEvent = new KeyEvent(KeyEvent.KEY_RELEASED, null, keyCode.getName(), keyCode, false, false,
false, false);
target.fireEvent(pressEvent);
target.fireEvent(releaseEvent);
}
public void typeKey(String key, Node target) {
KeyEvent event = new KeyEvent(KeyEvent.KEY_TYPED, key, null, null, false, false, false, false);
target.fireEvent(event);
}
You will need to simulate the KEY_TYPED event as well, for keys that type a character. That's just how the framework works. It is possible to press and release keys without typing a character (for example if CTRL was already down), so they are separate events. You can also get multiple KEY_TYPED events if a key is held down.