diff --git a/rta/src/main/java/com/gluonhq/richtextarea/RichTextAreaSkin.java b/rta/src/main/java/com/gluonhq/richtextarea/RichTextAreaSkin.java index 70f38cd..bac0dcc 100644 --- a/rta/src/main/java/com/gluonhq/richtextarea/RichTextAreaSkin.java +++ b/rta/src/main/java/com/gluonhq/richtextarea/RichTextAreaSkin.java @@ -86,6 +86,8 @@ import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.DragEvent; import javafx.scene.input.Dragboard; +import javafx.scene.input.InputMethodEvent; +import javafx.scene.input.InputMethodRequests; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; @@ -450,6 +452,9 @@ protected void invalidated() { private final ChangeListener tableAllowedListener; private final ChangeListener skinToneChangeListener; + private final EventHandler inputMethodTextChangedHandler = this::handleInputMethodEvent; + private final InputMethodRequests inputMethodRequests = createIMRequests(); + private final ResourceBundle resources; private class RichVirtualFlow extends VirtualFlow> { @@ -703,6 +708,8 @@ public void dispose() { getSkinnable().tableAllowedProperty().removeListener(tableAllowedListener); getSkinnable().setOnKeyPressed(null); getSkinnable().setOnKeyTyped(null); + getSkinnable().setInputMethodRequests(null); + getSkinnable().setOnInputMethodTextChanged(null); getSkinnable().widthProperty().removeListener(controlPrefWidthListener); getSkinnable().focusedProperty().removeListener(focusListener); getSkinnable().removeEventHandler(DragEvent.ANY, dndHandler); @@ -750,6 +757,9 @@ private void setup(Document document) { viewModel.setTableAllowed(getSkinnable().isTableAllowed()); getSkinnable().setOnKeyPressed(this::keyPressedListener); getSkinnable().setOnKeyTyped(this::keyTypedListener); + // Note both setOnInputMethodTextChanged() and setInputMethodRequests() are required for IME to work + getSkinnable().setOnInputMethodTextChanged(inputMethodTextChangedHandler); + getSkinnable().setInputMethodRequests(inputMethodRequests); getSkinnable().widthProperty().addListener(controlPrefWidthListener); getSkinnable().focusedProperty().addListener(focusListener); getSkinnable().addEventHandler(DragEvent.ANY, dndHandler); @@ -813,7 +823,11 @@ private void refreshTextFlow() { requestLayout(); nonTextNodesCount = nonTextNodes.get(); } - getSkinnable().requestFocus(); + if (getSkinnable().getScene() != null && getSkinnable().getScene().getWindow() != null && + getSkinnable().getScene().getFocusOwner() != null) { + // don't request focus if scene doesn't have a focus owner yet, + getSkinnable().requestFocus(); + } } finally { objectsCacheEvictionTimer.start(); } @@ -938,6 +952,43 @@ private void keyTypedListener(KeyEvent e) { } } + private InputMethodRequests createIMRequests() { + // dummy implementation for now + return new InputMethodRequests() { + @Override + public Point2D getTextLocation(int offset) { + return Point2D.ZERO; + } + + @Override + public String getSelectedText() { + return null; + } + + @Override + public int getLocationOffset(int x, int y) { + return 0; + } + + @Override + public void cancelLatestCommittedText() { + } + }; + } + + private void handleInputMethodEvent(InputMethodEvent event) { + final RichTextArea control = getSkinnable(); + if (control.isEditable() && !control.isDisabled()) { + // Insert committed text + String committed = event.getCommitted(); + if (!committed.isEmpty()) { + control.setOnInputMethodTextChanged(null); + execute(ACTION_CMD_FACTORY.insertText(committed)); + control.setOnInputMethodTextChanged(inputMethodTextChangedHandler); + } + } + } + private void populateContextMenu(boolean isEditable) { if (isEditable && editableContextMenuItems == null) { editableContextMenuItems = FXCollections.observableArrayList(