Search code examples
javaswingjtextarearight-to-left

Determining what text exists in an area of a JTextArea when the text is right to left


I have some code which tries to determine what text is within a given vertical slice of a text area where the vertical slice is specified as Y coordinates instead of lines.

Converting to using line maths is a fine workaround for this issue, by the way, so that is what I'm going to go with, but I can envisage cases where you might only have a Y coordinate and it seems like this sort of thing would come up so I'm going to ask it anyway.

I reduced my issue down to a fairly minimalist (lol Java) example. We display a frame with some text and then attempt to determine the character offset of the text closest to the start of the text area. We know from commonsense that it will be 0, but figuring that out programmatically is the problem.

public static void main(String[] args) throws Exception {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new RtlTest().run();
        }
    });
}

JFrame frame;
JTextArea textArea;

public void run() {
    textArea = new JTextArea(
        "\u05D4\u05D5\u05D3\u05E2\u05EA \u05D8\u05D9\u05D9\u05D2\u05E8 " +
        "\u05D8\u05E7\u05E1\u05D8 \u05D1\u05E2\u05D1\u05E8\u05D9\u05EA");

    frame = new JFrame();
    frame.setLayout(new BorderLayout());
    frame.add(new JScrollPane(textArea), BorderLayout.CENTER);
    frame.setSize(400, 300);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setVisible(true);
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            measure();
        }
    });
}

public void measure() {
    try {
        System.out.println("Either the line is left to right or it's right to left " +
                           "(or a mix), so one of these two values should be 0:");
        System.out.println(textArea.viewToModel(new Point(0, 0)));
        System.out.println(textArea.viewToModel(new Point(textArea.getWidth() - 1, 0)));

        Rectangle firstLetterView = textArea.modelToView(0);
        System.out.println("This one should definitely be 0, right? " +
                           "I mean, we got the coordinates from Swing itself:");
        System.out.println(textArea.viewToModel(new Point(firstLetterView.x,
                                                          firstLetterView.y)));

        frame.dispose();
    } catch (BadLocationException e) {
        throw new IllegalStateException(e);
    }
}

The output is rather surprising:

Either the line is left to right or it's right to left (or a mix),
    so one of these two values should be 0:
23
23
This one should definitely be 0, right? I mean, we got the coordinates from Swing itself:
24

Points of surprise:

  1. The character closest to the top left corner is not character 0.
  2. The character closest to the top right corner is not character 0.
  3. The character closest to the position of character 0 is not character 0.

Solution

  • Add the following line in your code:

    textArea.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    

    Your output numbers will change - but your text will show from the right side of the text area. I hope this is what you want.

    EDIT:

    According to this Hebrew and Arabic should be in RT orientation.