import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Change extends Application {

	public static void main(String[] args) {
		launch(args);
	}

	@Override
	public void start(Stage stage) {

		TextField tf = new TextField();

		StringProperty sp = new SimpleStringProperty();

		tf.textProperty().addListener((obs, oldValue, newValue) -> {
			sp.set(newValue);
		});

		sp.addListener(obs -> System.out.println("invalid"));

		// Print value of sp every 5 seconds on JavaFX thread
		Thread.startVirtualThread(() -> {
			while (true) {
				try {
					Thread.sleep(5000);
				} catch (InterruptedException ex) {
					// Ignore interruptions
				}
				Platform.runLater(() -> {
					System.out.println("runLater - text: " + sp.get());
				});
			}
		});

		VBox root = new VBox(10, tf);
		stage.setScene(new Scene(root, 300, 80));
		stage.setTitle("Example 1: StringProperty Listener");
		stage.show();
	}

}