diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f40bd8..e8b39b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## v0.1.5 - Improve behavior of `FXUtils.installSelectAllOrNoneMenu` (https://github.com/qupath/qupath/issues/1498) +- New `FXUtils.addCloseWindowShortcuts` methods to enable closing windows from the keyboard ## v0.1.4 - Make yes/no/cancel dialog non-resizable, for consistency (https://github.com/qupath/qupath-fxtras/issues/26) diff --git a/src/main/java/qupath/fx/utils/FXUtils.java b/src/main/java/qupath/fx/utils/FXUtils.java index 3f59744..e65fd18 100644 --- a/src/main/java/qupath/fx/utils/FXUtils.java +++ b/src/main/java/qupath/fx/utils/FXUtils.java @@ -27,12 +27,17 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.*; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.stage.Screen; import javafx.stage.Stage; import javafx.stage.Window; +import javafx.stage.WindowEvent; import org.controlsfx.control.CheckComboBox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +45,7 @@ import java.text.NumberFormat; import java.text.ParsePosition; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; @@ -556,6 +562,42 @@ public static void simplifyTitledPane(TitledPane pane, boolean boldTitle) { } } + + /** + * Add shortcuts to close a window using the standard shortcuts (Shortcut+W, Esc). + * @param stage + * @see #addCloseWindowShortcuts(Stage, Collection) + */ + public static void addCloseWindowShortcuts(Stage stage) { + addCloseWindowShortcuts(stage, Arrays.asList( + new KeyCodeCombination(KeyCode.W, KeyCodeCombination.SHORTCUT_DOWN), + new KeyCodeCombination(KeyCode.ESCAPE) + )); + } + + /** + * Add shortcuts to close a window using the specified key combinations. + * These are applied as key released events, so they will not interfere with text input. + * @param stage + * @param keyCombinations + * @see #addCloseWindowShortcuts(Stage) + * @implSpec this only fires a window close request; any handler may still choose to consume the event quietly + */ + public static void addCloseWindowShortcuts(Stage stage, Collection keyCombinations) { + if (keyCombinations.isEmpty()) { + logger.warn("Empty list of close window shortcuts - ignoring request"); + return; + } + stage.addEventHandler(KeyEvent.KEY_RELEASED, e -> { + if (e.isConsumed()) + return; + if (keyCombinations.stream().anyMatch(kc -> kc.match(e))) { + stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST)); + } + }); + } + + /** * Enable an undecorated stage to be moved by clicking and dragging within it. * Requires the scene to be set. Note that this will set mouse event listeners.