1

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.

Community
  • 1
  • 1
Sameen
  • 729
  • 6
  • 19

3 Answers3

1

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 !

Community
  • 1
  • 1
Bo Halim
  • 1,716
  • 2
  • 17
  • 23
  • Thanks for your help - any specific reason why you used a `Label`? In my `ChangeListener` (see question) I added `location = newStr.length();` where `location` is a field declared exactly like in your code. I've also added the `focusedProperty` `ChangeListener` as you have, but during runtime it doesn't seem to be working. Any chance you could try it with a `TextField` instead of a `Label`? I know it shouldn't make a difference and all but it's just for clarity. – Sameen Feb 10 '17 at 12:01
  • I deleted the `Label`, It was not necessary look I updated my code, and it does not work in your code, that's why I added the `Platform.runLater()` ! – Bo Halim Feb 10 '17 at 12:03
1
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

0

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);
        }
    });
}
Florian
  • 11
  • 2
  • yeah, seems to be working .. you are aware that now the behavior on focusGained by mouse is incorrect (the caret should be at the mouse position)? Have a look at TextFieldBehavior on how to improve that :) Minor: the field name in your code snippet doesn't match .. – kleopatra Aug 28 '21 at 09:28
  • Thx for your feedback kleopatra. I fixed the naming of the txtField and removed the call to moveCaretToEnd() when gaining focus. The behaviour on focusGained should now be correct. – Florian Aug 29 '21 at 11:11
  • hmm .. now it scrolls to start of text on focus gained by tabbing from another control. Which may be acceptable, depending on requirements and OS standard behaviour :) – kleopatra Aug 29 '21 at 13:49