Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correct Greek accented characters in a TextField

Tags:

java

javafx

The problem I am facing is when I have an input (TextField, TextArea, etc.) and I am trying to insert some Greek words which contain accented characters. Words like " Γάτα " (cat) works fine. Unfortunately, I am unable to type words like " Ταϊτή " (Tahiti). If I try to copy and paste it or hard code it, it is working fine, but when I try to write the word using the combination of

Shift + ';' + ι

which should produce the 'ϊ', instead I am getting '¨ι'

Here is an example :

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Example extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {

        VBox mainBox = new VBox(10);
        mainBox.setPadding(new Insets(20));
        mainBox.setAlignment(Pos.CENTER);

        // Ταϊτή = Tahiti
        TextField field1 = new TextField("Ταϊτή");
        TextField field2 = new TextField();

        mainBox.getChildren().add(field1);
        mainBox.getChildren().add(field2);

        field1.setStyle("-fx-font-size:24px");
        field2.setStyle("-fx-font-size:24px");

        stage.setScene(new Scene(mainBox));
        stage.show();
    }
}

And the image below show what I have as a result when i type the word myself

enter image description here

Installed JRE : jre1.8.0_221

Installed JDK : jdk1.8.0_221

To exclude the IDE (Eclipse) as a cause of the problem, I tested this behavior on SceneBuilder with just an AnchorPane and a TextField and I had the same issue there as well. But for the sake of argument, I have the editor and all the settings in the Eclipse to use the encoding UTF-8.

So I would like to ask: Is this a bug of the current JRE/JDK and if so, is there a well-known solution I could use or I should just use a Listener to catch the input and correct it myself?

Edit : As Sedrick point it out I could use the combination of alt + 0 2 3 9 but this is going to produce a different kind of letter which is similar to the Greek one but not the same. Look the image below. Unfortunately this is not the behavior than my client wants to, because the correct way to type it ( in Greek keyboards ) is with the : Shift + ';' + ι , where the 'ι' is the English letter 'i'.

enter image description here

Edit 2: Also I could hack my way around it by using the code below but if i decided to do so, I must do it for all of my TextFields which is something i would like to avoid. Otherwise I should create a "dummy" CustomTextField class which will extends the TextField and implement the hack there, then I could replace all the TextField references on my project with the CustomTextField.

field2.setTextFormatter(new TextFormatter<>(c -> {
    String text = c.getControlNewText();

    if (text.contains("¨ι") || text.contains("¨Ι")) { 

        // In order to "catch" a word with multiple wrong characters
        // for example if someone tries to copy/paste, I will use
        // the replaceAll() method
        text = text.replaceAll("¨ι", "ϊ");
        text = text.replaceAll("¨Ι", "Ϊ"); 

        // update the field 
        field2.setText(text);

        // correct the caret and anchor positions
        c.setCaretPosition(c.getCaretPosition() - 1);
        c.setAnchor(c.getCaretPosition());

        c.setText(""); // consume change because we already update it
    }
    return c;
}));
like image 673
JKostikiadis Avatar asked Aug 28 '19 15:08

JKostikiadis


1 Answers

I can reproduce the behaviour. If you add an EventHandler to your stage you can clearly see the problem.

stage.addEventFilter(KeyEvent.KEY_TYPED, ev -> {
    System.out.println("Key typed: " + ev.getCharacter());
});

Output:

Key typed: ¨
Key typed: Ι
Key typed: ¨
Key typed: ι

There is a resolved ticket describing this problem. If its a Glass Windowing Toolkit issue we cannot do much about it, but file a bug report.

I don't know if you can catch the typed keys before these KeyEvents are sent but you can filter them at stage level.

stage.addEventFilter(KeyEvent.KEY_TYPED, new KeyTypedListener());

This is not nice but a more "global" way to change behaviour.

public class KeyTypedListener implements EventHandler<KeyEvent> {

    private boolean disabled = false;
    private Map<String, String> charCombinations = new HashMap<>();
    private KeyEvent pendingEvent;

    public KeyTypedListener() {
        charCombinations.put("¨ι", "ϊ");
        charCombinations.put("¨Ι", "Ϊ");
    }

    @Override
    public void handle(final KeyEvent event) {

        if (disabled || event.getCharacter() == null
                || event.getCharacter().length() != 1) {
            return;
        }

        final String typed = event.getCharacter();

        if (pendingEvent == null && isCombiCharacter(typed)) {
            pendingEvent = event.copyFor(event.getSource(), event.getTarget());
            event.consume();

        } else if (pendingEvent != null) {
            String combination =
                    charCombinations.get(pendingEvent.getCharacter() + typed);

            if (combination == null) {
                disabled = true;
                fireEvent(pendingEvent);
                disabled = false;
                pendingEvent = null;
            } else {
                event.consume();
                pendingEvent = null;

                Platform.runLater(() -> {
                    fireEventWithCharacter(event, combination);
                });
            }
        }
    }

    private boolean isCombiCharacter(final String character) {
        return "¨".equals(character);
    }

    private void fireEvent(final KeyEvent event) {
        Event.fireEvent(event.getTarget(), event);
    }

    private void fireEventWithCharacter(final KeyEvent event,
            final String character) {
        fireEvent(new KeyEvent(event.getSource(), event.getTarget(),
                event.getEventType(), character, "", KeyCode.UNDEFINED,
                event.isShiftDown(), event.isControlDown(), event.isAltDown(),
                event.isMetaDown()));
    }
}

Of course you could also create a new EventDispatcher.

like image 187
Lesurglesum Avatar answered Nov 10 '22 14:11

Lesurglesum