Sameen
Sameen

Reputation: 734

JavaFX TextField: How to "auto-scroll" to right when text overflows

I'm using a TextField to display the path of a directory the user has opened in my application.

Currently, if the path can't fit inside the TextField, upon focusing away/clicking away from this control, it looks like as if the path has become truncated:

file path text overflowing and looks as if it's truncated

I want the behaviour of TextField set such that when I focus away from it, the path shown inside automatically scrolls to the right and the user is able to see the directory they've opened. I.e. something like this:

file path text auto scrolling and we can see the innermost directory

How can I achieve this? I've tried adapting the answer given from here

as follows in initialize() method in my FXML Controller class:

// Controller class fields
@FXML TextField txtMoisParentDirectory;
private String moisParentDirectory;

// ...

txtMoisParentDirectory.textProperty().addListener(new ChangeListener<String>() {

                @Override
                public void changed(ObservableValue<? extends String> observable, String oldStr, String newStr) {
                    moisParentDirectory = newStr;
                    txtMoisParentDirectory.selectPositionCaret(moisParentDirectory.length());
                    txtMoisParentDirectory.deselect();

                }
            });

However it doesn't work.

Upvotes: 1

Views: 2611

Answers (3)

Florian
Florian

Reputation: 61

Since the other answers didn't work for me here is a solution that should do the trick:

private TextField txtField;

// Both ChangeListeners just call moveCaretToEnd(), we need them both because of differing data types we are listening to
private final ChangeListener<Number> caretChangeListener = (observable, oldValue, newValue) -> moveCaretToEnd();
private final ChangeListener<String> textChangeListener = (observable, oldValue, newValue) -> moveCaretToEnd();

// This method moves the caret to the end of the text
private void moveCaretToEnd() {
     Platform.runLater(() -> {
         txtField.deselect();
         txtField.end();
     });
}

public void initialize() {
    // Immediatly add the listeners on initialization (or once you created the TextField if you are not using FXML)
    txtField.caretPositionProperty().addListener(caretChangeListener);
    txtField.textProperty().addListener(textChangeListener);

    txtField.focusedProperty().addListener((observable, oldValue, isFocused) -> {
        if (isFocused) {
            // once the TextField has been focused remove the listeners to enable normal editing of the text
            txtField.caretPositionProperty().removeListener(caretChangeListener);
            txtField.textProperty().removeListener(textChangeListener);
        } else {
            // when the focus is lost apply the listeners again
            moveCaretToEnd();
            txtField.caretPositionProperty().addListener(caretChangeListener);
            txtField.textProperty().addListener(textChangeListener);
        }
    });
}

Upvotes: 1

user10521589
user10521589

Reputation: 11

tf.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            int location = tf.getText().length();
            Platform.runLater(() -> {
                tf.positionCaret(location);
            });
        }
    });

this is also work

Upvotes: 1

Bo Halim
Bo Halim

Reputation: 1776

Your problem is based on two events, the length of the text entered and the loss of focus, so to solve it I used the properties textProperty() and focusedProperty() and here is the result :

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Launcher extends Application{

private Pane root = new Pane();
private Scene scene;
private TextField tf = new TextField();
private TextField tft = new TextField();

private int location = 0;

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


    scrollChange(); 
    tft.setLayoutX(300);
    root.getChildren().addAll(tft,tf);
    scene = new Scene(root,400,400);
    stage.setScene(scene);
    stage.show();

}

private void scrollChange(){


    tf.textProperty().addListener(new ChangeListener<String>() {

        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {

            location = tf.getText().length();

        }
    });


    tf.focusedProperty().addListener(new ChangeListener<Boolean>() {

        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {

            if(!newValue){

                Platform.runLater( new Runnable() {
                    @Override
                    public void run() {

                        tf.positionCaret(location);

                    }
                });


            }


        }
    });



}


public static void main(String[] args) {

    launch(args); 

}


}

And concerning the Platform.runLater I added it following this answer Here I don't know why it does not work without it, good luck !

Upvotes: 1

Related Questions