Reputation: 1035
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
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
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
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