user2821023
user2821023

Reputation: 511

Javafx listen for Control changes in a Panel

I want to add a change listener for every control in a panel. I want it to be robust, so whenever I change fxml file, I wouldn't have to alter the code that listens for changes in controls.

I have figured a way to add a listener for a specific type of control.

panel.getChildren()
     .stream()
     .filter(node -> node instanceof TextField).forEach(node ->
            ((TextField) node).textProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

However I would need to a add similar code to every type of control I intend to use in a panel for this to work.

panel.getChildren()
     .stream()
     .filter(node -> node instanceof TextField).forEach(node ->
            ((TextField) node).textProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

panel.getChildren()
     .stream()
     .filter(node -> node instanceof TextArea).forEach(node ->
            ((TextArea) node).textProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

//and so on...

panel.getChildren()
     .stream()
     .filter(node -> node instanceof ComboBox).forEach(node ->
            ((ComboBox<?>) node).valueProperty()
                .addListener((observable, oldValue, newValue) -> {
                        //execute some code
                 }));

What I want to do is.

I've a document editor that has a panel with controls, so whenever user changes one of that controls preset value, panel with save and cancel buttons would be enabled. Also if user tried to exit a program, without canceling or saving a document, a warning would pop up asking him if he wants to discard changes and exit or cancel.

However I intend to do a lot of changes to document structure, so I will need to add and remove controls from panel constantly. So I need the best way to add this type of listener for every control in a panel in one go.

Upvotes: 1

Views: 758

Answers (1)

guleryuz
guleryuz

Reputation: 2734

you should write (extend) your own controls and use them in your application. In them you can implement all specific tracking logic as @James_D mentioned. Such an extended TextField should look like this:

public class TrackableTextField extends javafx.scene.control.TextField {

    private StringProperty originalText = new ReadOnlyStringWrapper(this, "originalText");
    public final String getOriginalText() { return originalText.get(); }
    public final void setOriginalText(String value) { 
        originalText.set(value);
        setText(value);
    }
    public final StringProperty originalTextProperty() { return originalText; }

    private final ReadOnlyBooleanWrapper dirty = new ReadOnlyBooleanWrapper(this, "dirty", false);
    public final boolean isDirty() { return dirty.get(); }
    public final ReadOnlyBooleanProperty dirtyProperty() { return dirty.getReadOnlyProperty(); }

    public TrackableTextField() {
        init();
    }

    public TrackableTextField(String text) {
        init();
        setOriginalText(text);
    }

    private void init() {
        textProperty().addListener( e -> {
            dirty.set(!Objects.equals(getOriginalText(), getText()));
        } );
    }

    public void rollback() {
        setText(getOriginalText());
    }

    public void commit() {
        setOriginalText(getText());
    }
}

and a usage example may be like

public class Test extends Application {

    private TrackableTextField tf_name = new TrackableTextField();
    private TrackableTextField tf_sname = new TrackableTextField();

    private Button save = new Button("Save");
    private Button discard = new Button("Discard");

    @Override
    public void start(Stage primaryStage) {

        GridPane root = new GridPane();

        root.add(new Label("Name: "), 0, 0);
        root.add(tf_name, 1, 0);

        root.add(new Label("Surname: "), 0, 1);
        root.add(tf_sname, 1, 1);

        root.add(save, 0, 2);
        root.add(discard, 1, 2);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setScene(scene);
        primaryStage.show();

        initialize();

    }

    private void initialize() {

        save.setDisable(true);
        discard.setDisable(true);

        save.disableProperty().bind(tf_name.dirtyProperty().or(tf_sname.dirtyProperty()).not());
        discard.disableProperty().bind(tf_name.dirtyProperty().or(tf_sname.dirtyProperty()).not());

        tf_name.setOriginalText("guleryuz");
        tf_sname.setOriginalText("guleryuz");

        save.setOnAction( e -> {
            tf_name.commit();
            tf_sname.commit();
        } );

        discard.setOnAction( e -> {
            tf_name.rollback();
            tf_sname.rollback();
        } );

    }

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

}

Upvotes: 2

Related Questions