diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/FilePickerDialog.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/FilePickerDialog.java new file mode 100644 index 0000000..01b517e --- /dev/null +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/FilePickerDialog.java @@ -0,0 +1,33 @@ +package de.gecheckt.pdf.umbenenner.adapter.in.gui; + +import java.util.List; + +import javafx.stage.FileChooser; + +/** + * Funktionales Interface fuer den Datei-Auswaehldialog der GUI. + *

+ * Kapselt die Abhaengigkeit zum nativen {@link FileChooser} in einem + * injizierbaren Hook, der in Tests durch eine einfache Lambda-Implementierung + * ersetzt werden kann. Die Standardimplementierung oeffnet einen echten + * nativen Datei-Dialog; Test-Stubs koennen einen festen Pfad zurueckgeben + * oder {@code null} simulieren (Abbrechen). + *

+ * Im Gegensatz zur frueheren {@code BiFunction}-Variante nimmt dieser Hook + * auch die Liste der {@link FileChooser.ExtensionFilter} entgegen, damit der + * native Dialog die Filter tatsaechlich anwenden kann. + */ +@FunctionalInterface +interface FilePickerDialog { + + /** + * Oeffnet den Datei-Auswaehldialog und gibt den ausgewaehlten absoluten + * Pfad zurueck. + * + * @param title der Titel des Dialogs + * @param initialPath der Anfangspfad als Hinweis; darf leer oder {@code null} sein + * @param filters Liste der Dateitypfilter; darf leer sein, aber nicht {@code null} + * @return der ausgewaehlte absolute Pfad als String, oder {@code null} wenn abgebrochen + */ + String pick(String title, String initialPath, List filters); +} diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java index ef96b33..eebddd6 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java @@ -235,17 +235,16 @@ public final class GuiConfigurationEditorWorkspace { this::showNativeDirectoryChooser; /** - * Dialog function for file-picker buttons; package-private to allow substitution in tests. - * Receives the dialog title and the current field text (used as an initial path hint), and - * returns the selected absolute path string or {@code null} when the dialog is cancelled. + * Dialog-Hook fuer Datei-Auswaehldialoge; package-private, damit Tests eine Stub-Implementierung + * injizieren koennen. Empfaengt Titel, aktuellen Feldtext (als Anfangspfad-Hinweis) und die + * Liste der Dateitypfilter; gibt den ausgewaehlten absoluten Pfad oder {@code null} zurueck. *

- * The default implementation opens a native {@link FileChooser}. Tests may replace - * this with a lambda that returns a fixed string without opening a native dialog. - *

- * Extension filters are applied by the default implementation only; test stubs bypass them. + * Die Standardimplementierung oeffnet einen nativen {@link FileChooser} und wendet die + * uebergebenen Filter an. Test-Stubs koennen die Filter ignorieren und einen festen Pfad + * zurueckgeben. */ - java.util.function.BiFunction filePickerDialog = - (title, initialPath) -> showNativeFileChooser(title, initialPath); + FilePickerDialog filePickerDialog = + (title, initialPath, filters) -> showNativeFileChooser(title, initialPath, filters); /** * Guard that mediates the protection dialog before destructive actions. @@ -2462,20 +2461,21 @@ public final class GuiConfigurationEditorWorkspace { } /** - * Opens a file-picker dialog using the injectable {@link #filePickerDialog} hook. + * Oeffnet einen Datei-Auswaehldialog ueber den injizierbaren {@link #filePickerDialog}-Hook. *

- * In production the hook delegates to a native {@link FileChooser}. In tests the hook - * can be replaced with a lambda that returns a fixed string. Windows mapped drive letters - * are preserved unchanged. + * In der Produktion delegiert der Hook an einen nativen {@link FileChooser} und wendet + * die uebergebenen Filter an. In Tests kann der Hook durch eine Lambda-Implementierung + * ersetzt werden, die einen festen Pfad zurueckgibt. Windows-Laufwerksbuchstaben + * (z. B. {@code S:\}) werden unveraendert weitergegeben. * - * @param title the dialog title - * @param initialPath the pre-selected path text; may be empty or {@code null} - * @param filters extension filters (only applied by the native default implementation) - * @return the selected absolute path string, or {@code null} when the dialog was cancelled + * @param title der Titel des Datei-Dialogs + * @param initialPath der Anfangspfad als Hinweis; darf leer oder {@code null} sein + * @param filters Dateitypfilter, die im nativen Dialog angezeigt werden + * @return den ausgewaehlten absoluten Pfad, oder {@code null} wenn der Dialog abgebrochen wurde */ private String pickFile(String title, String initialPath, FileChooser.ExtensionFilter... filters) { - return filePickerDialog.apply(title, initialPath); + return filePickerDialog.pick(title, initialPath, List.of(filters)); } /** @@ -2500,22 +2500,30 @@ public final class GuiConfigurationEditorWorkspace { } /** - * Default native file-chooser implementation used by {@link #filePickerDialog}. + * Standardimplementierung des nativen Datei-Auswaehldialogs fuer {@link #filePickerDialog}. + *

+ * Wendet alle uebergebenen Dateitypfilter auf den {@link FileChooser} an, bevor der Dialog + * geoeffnet wird. * - * @param title the dialog title - * @param initialPath the initial path hint; may be empty or {@code null} - * @return the selected absolute path string, or {@code null} when cancelled or unavailable + * @param title der Titel des Datei-Dialogs + * @param initialPath der Anfangspfad als Hinweis; darf leer oder {@code null} sein + * @param filters anzuwendende Dateitypfilter; darf leer, aber nicht {@code null} sein + * @return den ausgewaehlten absoluten Pfad, oder {@code null} wenn abgebrochen oder nicht verfuegbar */ - private String showNativeFileChooser(String title, String initialPath) { + private String showNativeFileChooser(String title, String initialPath, + List filters) { FileChooser chooser = new FileChooser(); chooser.setTitle(title); setInitialPathForFileChooser(chooser, initialPath); + if (!filters.isEmpty()) { + chooser.getExtensionFilters().addAll(filters); + } Window owner = root.getScene() == null ? null : root.getScene().getWindow(); try { File selected = chooser.showOpenDialog(owner); return selected == null ? null : selected.getAbsolutePath(); } catch (UnsupportedOperationException e) { - LOG.debug("GUI-Editor: Datei-Dialog nicht verf\u00fcgbar (headless)."); + LOG.debug("GUI-Editor: Datei-Dialog nicht verfuegbar (headless)."); return null; } } diff --git a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiEditorFieldBindingTest.java b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiEditorFieldBindingTest.java index 25d2622..99bf004 100644 --- a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiEditorFieldBindingTest.java +++ b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiEditorFieldBindingTest.java @@ -202,11 +202,11 @@ class GuiEditorFieldBindingTest { String originalSqlite = ws.editorState().values().sqliteFile(); // Replace the file-picker hook: always return null (cancel). - ws.filePickerDialog = (title, initialPath) -> null; + ws.filePickerDialog = (title, initialPath, filters) -> null; // Simulate button handler: null result means do nothing. - String picked = ws.filePickerDialog.apply("SQLite-Datei ausw\u00e4hlen", - ws.editorState().values().sqliteFile()); + String picked = ws.filePickerDialog.pick("SQLite-Datei ausw\u00e4hlen", + ws.editorState().values().sqliteFile(), java.util.List.of()); if (picked != null) { ws.editorState = ws.editorState() .withValues(ws.editorState().values().withSqliteFile(picked));