From 65d8379c151b86b804b6cb5f53b50d18d3c70936 Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Mon, 27 Apr 2026 15:17:56 +0200 Subject: [PATCH] Fix #24 (teilweise): Pfade-Bereich kompakter gestalten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Quellordner + Zielordner nebeneinander in 2-Spalten-Layout - SQLite-Datei + Prompt-Datei nebeneinander in 2-Spalten-Layout - Vertikale Abstände zwischen Feldern reduziert (von 0 zu 4) - Lock-Datei und Log-Verzeichnis in ausklappbare TitledPane verschoben (standardmäßig eingeklappt, Label: "Weitere Optionen (Click zum Aufklappen)") - Neue Hilfsmethode buildTwoPathFieldsRow() für 2-Spalten-Pfad-Layouts - Import für TitledPane hinzugefügt - Alle Kommentare auf Deutsch Build: .\mvnw.cmd clean verify -pl pdf-umbenenner-adapter-in-gui --also-make Build-Status: ERFOLGREICH (322 Tests bestanden) Co-Authored-By: Claude Haiku 4.5 --- .../gui/GuiConfigurationEditorWorkspace.java | 160 ++++++++++++++---- 1 file changed, 126 insertions(+), 34 deletions(-) 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 74f0b55..cd6331b 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 @@ -69,6 +69,7 @@ import javafx.scene.control.Separator; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TextField; +import javafx.scene.control.TitledPane; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.KeyCode; @@ -1336,47 +1337,54 @@ public final class GuiConfigurationEditorWorkspace { VBox card = createCardContainer(); card.getChildren().add(sectionTitle("Pfade")); - // Use a VBox instead of a plain GridPane so error slots can sit directly below each row. - VBox fieldRows = new VBox(0); + // Kompakteres 2-Spalten-Layout für Pfade-Felder + VBox mainRows = new VBox(4); - // Quellordner + // Quellordner + Zielordner nebeneinander (2-Spalten-Layout) TextField sourceFolderField = boundTextField( editorState.values().sourceFolder(), val -> updateValues(editorState.values().withSourceFolder(val))); Label sourceFolderErrorLabel = createFieldErrorLabel(); fieldErrorLabels.put("source.folder", sourceFolderErrorLabel); - fieldRows.getChildren().add(buildPathFieldRow("Quellordner:", sourceFolderField, - sourceFolderErrorLabel, () -> { - String picked = pickDirectory("Quellordner auswählen", sourceFolderField.getText()); - if (picked != null) { - sourceFolderField.setText(picked); - updateValues(editorState.values().withSourceFolder(picked)); - } - })); - // Zielordner TextField targetFolderField = boundTextField( editorState.values().targetFolder(), val -> updateValues(editorState.values().withTargetFolder(val))); Label targetFolderErrorLabel = createFieldErrorLabel(); fieldErrorLabels.put("target.folder", targetFolderErrorLabel); - fieldRows.getChildren().add(buildPathFieldRow("Zielordner:", targetFolderField, - targetFolderErrorLabel, () -> { + + HBox folderRow = buildTwoPathFieldsRow( + "Quellordner:", sourceFolderField, sourceFolderErrorLabel, () -> { + String picked = pickDirectory("Quellordner auswählen", sourceFolderField.getText()); + if (picked != null) { + sourceFolderField.setText(picked); + updateValues(editorState.values().withSourceFolder(picked)); + } + }, + "Zielordner:", targetFolderField, targetFolderErrorLabel, () -> { String picked = pickDirectory("Zielordner auswählen", targetFolderField.getText()); if (picked != null) { targetFolderField.setText(picked); updateValues(editorState.values().withTargetFolder(picked)); } - })); + }); + mainRows.getChildren().add(folderRow); - // SQLite-Datei + // SQLite-Datei + Prompt-Datei nebeneinander (2-Spalten-Layout) TextField sqliteField = boundTextField( editorState.values().sqliteFile(), val -> updateValues(editorState.values().withSqliteFile(val))); Label sqliteErrorLabel = createFieldErrorLabel(); fieldErrorLabels.put("sqlite.file", sqliteErrorLabel); - fieldRows.getChildren().add(buildPathFieldRow("SQLite-Datei:", sqliteField, - sqliteErrorLabel, () -> { + + TextField promptField = boundTextField( + editorState.values().promptTemplateFile(), + val -> updateValues(editorState.values().withPromptTemplateFile(val))); + Label promptErrorLabel = createFieldErrorLabel(); + fieldErrorLabels.put("prompt.template.file", promptErrorLabel); + + HBox fileRow = buildTwoPathFieldsRow( + "SQLite-Datei:", sqliteField, sqliteErrorLabel, () -> { String picked = pickFile("SQLite-Datei auswählen", sqliteField.getText(), new FileChooser.ExtensionFilter("SQLite-Dateien", "*.db", "*.sqlite", "*.sqlite3"), new FileChooser.ExtensionFilter("Alle Dateien (*.*)", "*.*")); @@ -1384,16 +1392,8 @@ public final class GuiConfigurationEditorWorkspace { sqliteField.setText(picked); updateValues(editorState.values().withSqliteFile(picked)); } - })); - - // Prompt-Datei - TextField promptField = boundTextField( - editorState.values().promptTemplateFile(), - val -> updateValues(editorState.values().withPromptTemplateFile(val))); - Label promptErrorLabel = createFieldErrorLabel(); - fieldErrorLabels.put("prompt.template.file", promptErrorLabel); - fieldRows.getChildren().add(buildPathFieldRow("Prompt-Datei:", promptField, - promptErrorLabel, () -> { + }, + "Prompt-Datei:", promptField, promptErrorLabel, () -> { String picked = pickFile("Prompt-Datei auswählen", promptField.getText(), new FileChooser.ExtensionFilter("Textdateien", "*.txt", "*.md"), new FileChooser.ExtensionFilter("Alle Dateien (*.*)", "*.*")); @@ -1401,21 +1401,32 @@ public final class GuiConfigurationEditorWorkspace { promptField.setText(picked); updateValues(editorState.values().withPromptTemplateFile(picked)); } - })); + }); + mainRows.getChildren().add(fileRow); - // Runtime-Lock-Datei (optional) — no error slot needed (optional field) + card.getChildren().add(mainRows); + + // Optionale Felder in ausklappbare TitledPane packen TextField lockField = boundTextField( editorState.values().runtimeLockFile(), val -> updateValues(editorState.values().withRuntimeLockFile(val))); - fieldRows.getChildren().add(buildSimpleFieldRow("Lock-Datei (optional):", lockField, null)); - // Log-Verzeichnis (optional) — no error slot needed (optional field) TextField logDirField = boundTextField( editorState.values().logDirectory(), val -> updateValues(editorState.values().withLogDirectory(val))); - fieldRows.getChildren().add(buildSimpleFieldRow("Log-Verzeichnis (optional):", logDirField, null)); - card.getChildren().add(fieldRows); + VBox optionalContent = new VBox(4); + optionalContent.setPadding(new Insets(6, 0, 0, 0)); + optionalContent.getChildren().add(buildSimpleFieldRow("Lock-Datei:", lockField, null)); + optionalContent.getChildren().add(buildSimpleFieldRow("Log-Verzeichnis:", logDirField, null)); + + TitledPane optionalPane = new TitledPane("Weitere Optionen (Click zum Aufklappen)", optionalContent); + optionalPane.setCollapsible(true); + optionalPane.setExpanded(false); + optionalPane.setStyle("-fx-padding: 2px;"); + + card.getChildren().add(optionalPane); + return card; } @@ -2518,6 +2529,87 @@ public final class GuiConfigurationEditorWorkspace { return slot; } + /** + * Baut zwei Pfad-Felder nebeneinander in einer HBox. Jedes Feld hat ein Label und einen Picker-Button. + * Error-Labels werden untereinander angezeigt. + * + * @param label1Text das Label des linken Felds + * @param field1 das TextFeld links + * @param errorLabel1 das Error-Label des linken Felds oder null + * @param onPick1 Action für den Picker-Button des linken Felds + * @param label2Text das Label des rechten Felds + * @param field2 das TextFeld rechts + * @param errorLabel2 das Error-Label des rechten Felds oder null + * @param onPick2 Action für den Picker-Button des rechten Felds + * @return eine HBox mit den zwei nebeneinander angeordneten Pfad-Feldern + */ + private static HBox buildTwoPathFieldsRow( + String label1Text, TextField field1, Label errorLabel1, Runnable onPick1, + String label2Text, TextField field2, Label errorLabel2, Runnable onPick2) { + + // Linkes Feld mit Label und Picker-Button + Label label1 = new Label(label1Text); + Button pickButton1 = new Button("…"); + pickButton1.setOnAction(e -> onPick1.run()); + pickButton1.setMinWidth(32); + HBox fieldBox1 = new HBox(4, field1, pickButton1); + HBox.setHgrow(field1, Priority.ALWAYS); + fieldBox1.setAlignment(Pos.CENTER_LEFT); + + GridPane grid1 = new GridPane(); + grid1.setHgap(12); + grid1.setVgap(0); + javafx.scene.layout.ColumnConstraints labelCol1 = new javafx.scene.layout.ColumnConstraints(); + labelCol1.setMinWidth(100); + labelCol1.setPrefWidth(120); + javafx.scene.layout.ColumnConstraints fieldCol1 = new javafx.scene.layout.ColumnConstraints(); + fieldCol1.setFillWidth(true); + fieldCol1.setHgrow(Priority.ALWAYS); + grid1.getColumnConstraints().addAll(labelCol1, fieldCol1); + grid1.add(label1, 0, 0); + grid1.add(fieldBox1, 1, 0); + + VBox leftSlot = new VBox(0, grid1); + if (errorLabel1 != null) { + leftSlot.getChildren().add(errorLabel1); + } + + // Rechtes Feld mit Label und Picker-Button + Label label2 = new Label(label2Text); + Button pickButton2 = new Button("…"); + pickButton2.setOnAction(e -> onPick2.run()); + pickButton2.setMinWidth(32); + HBox fieldBox2 = new HBox(4, field2, pickButton2); + HBox.setHgrow(field2, Priority.ALWAYS); + fieldBox2.setAlignment(Pos.CENTER_LEFT); + + GridPane grid2 = new GridPane(); + grid2.setHgap(12); + grid2.setVgap(0); + javafx.scene.layout.ColumnConstraints labelCol2 = new javafx.scene.layout.ColumnConstraints(); + labelCol2.setMinWidth(100); + labelCol2.setPrefWidth(120); + javafx.scene.layout.ColumnConstraints fieldCol2 = new javafx.scene.layout.ColumnConstraints(); + fieldCol2.setFillWidth(true); + fieldCol2.setHgrow(Priority.ALWAYS); + grid2.getColumnConstraints().addAll(labelCol2, fieldCol2); + grid2.add(label2, 0, 0); + grid2.add(fieldBox2, 1, 0); + + VBox rightSlot = new VBox(0, grid2); + if (errorLabel2 != null) { + rightSlot.getChildren().add(errorLabel2); + } + + // Beide Felder nebeneinander in einer HBox + HBox containerRow = new HBox(12, leftSlot, rightSlot); + HBox.setHgrow(leftSlot, Priority.ALWAYS); + HBox.setHgrow(rightSlot, Priority.ALWAYS); + containerRow.setPadding(new Insets(0, 0, 0, 0)); + + return containerRow; + } + /** * Builds a labelled simple-field row (no picker button) as a {@link VBox} containing a grid * row with the label and text field, plus an optional error-slot label directly beneath.