import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.geometry.Insets;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.beans.property.ReadOnlyObjectWrapper;

public class ScreenChange extends Application {
    private final TextArea logArea = new TextArea("");
    private int lineNumber = 1;

    public static void main(String[] args) {
        Application.launch(ScreenChange.class, args);
    }

    @Override
    public void start(Stage stage) {
        logArea.setEditable(false);
        addToLog("Minimize or restore a window or toggle the fullscreen state.");
        addToLog("These actions can cause the Screen list to update.\n");

        // This code attempts to monitor changes to the Screen property
        // associated with the stage. This is a private field of the Window
        // class. Access will fail unless the test is compiled with
        // --add-opens javafx.graphics/javafx.stage=ALL-UNNAMED
        try {
            var f = stage.getClass().getSuperclass().getDeclaredField("screen");
            f.setAccessible(true);
            @SuppressWarnings("unchecked")
            ReadOnlyObjectWrapper<Screen> prop = (ReadOnlyObjectWrapper<Screen>) f.get(stage);
            prop.addListener((observable, oldValue, newValue) -> {
                addNumberedLineToLog("Window screen changed to");
                describeScreen(newValue);
            });
        }
        catch (Exception ex) {
            logArea.appendText("Not monitoring changes to Window.screen\n\n");
        }

        Screen.getScreens().addListener((ListChangeListener.Change<? extends Screen> change) -> {
            addNumberedLineToLog("List of screens changed");
            for (Screen screen : Screen.getScreens()) {
                describeScreen(screen);
            }
        });

        VBox root = new VBox();
        root.setPadding(new Insets(3));
        VBox.setVgrow(logArea, Priority.ALWAYS);
        root.getChildren().addAll(logArea);

        Scene scene = new Scene(root, 640, 640);
        stage.setScene(scene);
        stage.setTitle("Screen Changes");
        stage.show();
    }

    private void describeScreen(Screen screen) {
        var bounds = screen.getBounds();
        String desc = "        (" + (int) bounds.getMinX() + ", " + (int) bounds.getMinY() + ")";
        desc += " " + (int) bounds.getWidth() + "x" + (int) bounds.getHeight();
        addToLog(desc);
    }

    private void addToLog(String str) {
        logArea.appendText(str);
        logArea.appendText("\n");
    }

    private void addNumberedLineToLog(String str) {
        logArea.appendText(lineNumber + ". ");
        lineNumber += 1;
        addToLog(str);
    }
}
