Frage

I am trying to find a way to get the corresponding screen location of the caret-position in a text area in JavaFX. I need the location to show Popups in text at the caret location.

I found request or it here: https://bugs.openjdk.java.net/browse/JDK-8090849

and some workarounds here: https://community.oracle.com/thread/2534556

They work somehow, but there are a few issues with location not updating correctly sometimes. Does anyone have a suggestion of how to get the caret position in terms of screen X and Y?

War es hilfreich?

Lösung

Just wanted to follow-up with an answer to this question for TextField controls in JavaFX. I'm sure the same could apply to other text input controls as well. I got the idea from looking at some code that involved changing the default colour of the caret using a subclass of TextFieldSkin class. If you look closely, the TextFieldSkin superclass holds a reference to the Path instance which represents the caret in a protected field called caretPath. Although this is kind of a hack'ish solution, it does provide developers with the absolute coordinates of the Caret in a much safer way than most of the hacks I've seen out there.

public class TextFieldCaretControlSkin extends TextFieldSkin {
    public TextFieldCaretControlSkin(TextField textField, Stage stage) {
        super(textField);
        Popup popup = new Popup();

        // Make the popup appear to the right of the caret
        popup.setAnchorLocation(PopupWindow.AnchorLocation.CONTENT_BOTTOM_LEFT);
        // Make sure its position gets corrected to stay on screen if we go out of screen
        popup.setAutoFix(true);
        // Add list view (mock autocomplete popup)
        popup.getContent().add(new ListView<String>());

        // listen for changes in the layout bounds of the caret path
        caretPath.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
            @Override
            public void changed(ObservableValue<? extends Bounds> observable, 
                                Bounds oldValue, Bounds newValue) {
                popup.hide();
                // get the caret's x position relative to the textfield.
                double x = newValue.getMaxX(); 
                // get the caret's y position relative to the textfield.
                double y = newValue.getMaxY(); 
                Point2D p = caretPath.localToScene(x, y);

                /*
                 * If the coordinates are negatives then the Path is being
                 * redrawn and we should just skip further processing.
                 */
                if (x == -1.0 || y == -1.0)
                    return;

                // show the popup at these absolute coordinates.                    
                popup.show(textField,
                        p.getX() + caretPath.getScene().getX() + 
                             caretPath.getScene().getWindow().getX(),
                        p.getY() + caretPath.getScene().getY() + 
                             caretPath.getScene().getWindow().getY() - 
                             newValue.getHeight()); // put the popup on top of the caret
            }
        });
    }
}

To use you'd have to embed this in some sort of subclassed text input control and remember to do textField.setSkin(new TextFieldCaretControlSkin(textField)). There may be better ways to do this since I am not a JavaFX expert but I just wanted to share this solution with the rest of the world just in case it provided some insight.

Hope this helps!

Andere Tipps

This is how you use RichTextFX to position a popup window 4px to the right of the caret:

InlineCssTextArea area = new InlineCssTextArea();
Popup popup = new Popup();
popup.getContent().add(new Label("I am a popup label!"));
area.setPopupWindow(popup);
area.setPopupAlignment(PopupAlignment.CARET_CENTER);
area.setPopupAnchorOffset(new Point2D(4, 0));

You still need to control the visibility of the popup window yourself by calling

popup.show(ownerWindow);
popup.hide();

See also this working demo.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top