Jay Edwards
Jay Edwards

Reputation: 1035

JavaFX testing whether application is closing as a whole or just individual window

I have a JavaFX application that opens multiple windows. The data within a window is recorded to a database whenever the focus is lost from that window. When a user wants to close a window, an event handler should be fired that requests the tuple be deleted from the database. I only want this to occur when the user actively clicks the x in the top right of the window and not when the application is quit outright (e.g. if the program is quit from an OS start bar or equivalent) or halted in some other fashion.

the close event handler stub looks similar to the below:

foo.setOnCloseRequest(new EventHandler<WindowEvent>() 
  {      
    @Override
    public void handle(WindowEvent event)
    {          
      try
      {
        barController.exec(Action.DELETE, item);     
      } catch (Exception e)
      {
        e.printStackTrace();
      }
    }
  });

The trouble is, when I halt the program from the bar in Ubuntu (for example), this event is still being called for each window; and each time the tested event is WindowEvent.WINDOW_CLOSE_REQUEST whether the user or the application closed the window.

Simply put: is there some kind of way to delineate "onUserCloseWindow" from "onCloseApplication"?

Upvotes: 0

Views: 714

Answers (3)

Jay Edwards
Jay Edwards

Reputation: 1035

The best approach seemed to be to test if the stage had focus. pressing the X should always occur in focus. Therefore, if a close request is called on a focused window then a delete request is sent. This doesn't seem to consider actions like "Alt-F4" but that can be justified as a "feature". It isn't a commercial application, just a useful tool I wanted to write for graphical Linux environments.

if (foo.focusedProperty().getValue()) {
            try
            {
              barController.exec(Action.DELETE, fooId);
            } catch (Exception e)
            {
              e.printStackTrace();
            }
          }

Upvotes: 0

jewelsea
jewelsea

Reputation: 159566

It looks like, when you close from the Taskbar (at least on a Mac), the owner window, gets a close request event before the event is delivered to other child windows. So you could detect the close request event on the owner, set a flag and when the other windows receive their close requests they can check if the flag has been set before performing their processing. If the flag was set, then it was a task bar or user initiated shutdown of the complete application, otherwise it was a user initiated close of just a single secondary window.

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Eventacity extends Application {
    private boolean isPrimaryClosing = false;

    @Override
    public void start(Stage primary) {
        primary.setScene(new Scene(new Group(), 200, 200));
        primary.setTitle("Main");
        addEventHandlers(primary);
        primary.show();

        for (int i = 0; i < 3; i++) {
            Stage secondary = new Stage();
            secondary.setTitle("Secondary " + i);
            secondary.setScene(new Scene(new Group(), 100, 100));
            secondary.setX(primary.getX() + (i+1) * 25);
            secondary.setY(primary.getY() + (i+1) * 25);

            secondary.initOwner(primary);
            addEventHandlers(secondary);

            secondary.show();
        }
    }

    private void addEventHandlers(Stage stage) {
        stage.setOnCloseRequest(event -> {
            if (stage.getOwner() == null) {
                isPrimaryClosing = true;
            }

            System.out.println(
                    "Close Request:   " + stage.getTitle()
            );
            System.out.println(
                    "Primary Closing: " + isPrimaryClosing
            );

            if (!isPrimaryClosing) {
                // delete data from database.
            }
        });
        stage.setOnHiding(event -> System.out.println(
                "Hiding:          " + stage.getTitle())
        );
        stage.setOnHidden(event -> System.out.println(
                "Hid:             " + stage.getTitle())
        );
        stage.setOnShowing(event -> System.out.println(
                "Showing:         " + stage.getTitle())
        );
        stage.setOnShown(event -> System.out.println(
                "Showed:          " + stage.getTitle())
        );
    }

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

Upvotes: 1

Oliver Jan Krylow
Oliver Jan Krylow

Reputation: 1725

When using the native window decoration, I do not believe there is a way to distinguish these types of events. They are both, from the point of view of your app, an

external request to close this Window

.

I think your best option would be implementing your own window decoration as mentioned here.

Upvotes: 1

Related Questions