Fix #24 (teilweise): Pfade-Bereich kompakter gestalten
- 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 <noreply@anthropic.com>
This commit is contained in:
+126
-34
@@ -69,6 +69,7 @@ import javafx.scene.control.Separator;
|
|||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TabPane;
|
import javafx.scene.control.TabPane;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.TitledPane;
|
||||||
import javafx.scene.input.Clipboard;
|
import javafx.scene.input.Clipboard;
|
||||||
import javafx.scene.input.ClipboardContent;
|
import javafx.scene.input.ClipboardContent;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
@@ -1336,47 +1337,54 @@ public final class GuiConfigurationEditorWorkspace {
|
|||||||
VBox card = createCardContainer();
|
VBox card = createCardContainer();
|
||||||
card.getChildren().add(sectionTitle("Pfade"));
|
card.getChildren().add(sectionTitle("Pfade"));
|
||||||
|
|
||||||
// Use a VBox instead of a plain GridPane so error slots can sit directly below each row.
|
// Kompakteres 2-Spalten-Layout für Pfade-Felder
|
||||||
VBox fieldRows = new VBox(0);
|
VBox mainRows = new VBox(4);
|
||||||
|
|
||||||
// Quellordner
|
// Quellordner + Zielordner nebeneinander (2-Spalten-Layout)
|
||||||
TextField sourceFolderField = boundTextField(
|
TextField sourceFolderField = boundTextField(
|
||||||
editorState.values().sourceFolder(),
|
editorState.values().sourceFolder(),
|
||||||
val -> updateValues(editorState.values().withSourceFolder(val)));
|
val -> updateValues(editorState.values().withSourceFolder(val)));
|
||||||
Label sourceFolderErrorLabel = createFieldErrorLabel();
|
Label sourceFolderErrorLabel = createFieldErrorLabel();
|
||||||
fieldErrorLabels.put("source.folder", sourceFolderErrorLabel);
|
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(
|
TextField targetFolderField = boundTextField(
|
||||||
editorState.values().targetFolder(),
|
editorState.values().targetFolder(),
|
||||||
val -> updateValues(editorState.values().withTargetFolder(val)));
|
val -> updateValues(editorState.values().withTargetFolder(val)));
|
||||||
Label targetFolderErrorLabel = createFieldErrorLabel();
|
Label targetFolderErrorLabel = createFieldErrorLabel();
|
||||||
fieldErrorLabels.put("target.folder", targetFolderErrorLabel);
|
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());
|
String picked = pickDirectory("Zielordner auswählen", targetFolderField.getText());
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
targetFolderField.setText(picked);
|
targetFolderField.setText(picked);
|
||||||
updateValues(editorState.values().withTargetFolder(picked));
|
updateValues(editorState.values().withTargetFolder(picked));
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
mainRows.getChildren().add(folderRow);
|
||||||
|
|
||||||
// SQLite-Datei
|
// SQLite-Datei + Prompt-Datei nebeneinander (2-Spalten-Layout)
|
||||||
TextField sqliteField = boundTextField(
|
TextField sqliteField = boundTextField(
|
||||||
editorState.values().sqliteFile(),
|
editorState.values().sqliteFile(),
|
||||||
val -> updateValues(editorState.values().withSqliteFile(val)));
|
val -> updateValues(editorState.values().withSqliteFile(val)));
|
||||||
Label sqliteErrorLabel = createFieldErrorLabel();
|
Label sqliteErrorLabel = createFieldErrorLabel();
|
||||||
fieldErrorLabels.put("sqlite.file", sqliteErrorLabel);
|
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(),
|
String picked = pickFile("SQLite-Datei auswählen", sqliteField.getText(),
|
||||||
new FileChooser.ExtensionFilter("SQLite-Dateien", "*.db", "*.sqlite", "*.sqlite3"),
|
new FileChooser.ExtensionFilter("SQLite-Dateien", "*.db", "*.sqlite", "*.sqlite3"),
|
||||||
new FileChooser.ExtensionFilter("Alle Dateien (*.*)", "*.*"));
|
new FileChooser.ExtensionFilter("Alle Dateien (*.*)", "*.*"));
|
||||||
@@ -1384,16 +1392,8 @@ public final class GuiConfigurationEditorWorkspace {
|
|||||||
sqliteField.setText(picked);
|
sqliteField.setText(picked);
|
||||||
updateValues(editorState.values().withSqliteFile(picked));
|
updateValues(editorState.values().withSqliteFile(picked));
|
||||||
}
|
}
|
||||||
}));
|
},
|
||||||
|
"Prompt-Datei:", promptField, promptErrorLabel, () -> {
|
||||||
// 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, () -> {
|
|
||||||
String picked = pickFile("Prompt-Datei auswählen", promptField.getText(),
|
String picked = pickFile("Prompt-Datei auswählen", promptField.getText(),
|
||||||
new FileChooser.ExtensionFilter("Textdateien", "*.txt", "*.md"),
|
new FileChooser.ExtensionFilter("Textdateien", "*.txt", "*.md"),
|
||||||
new FileChooser.ExtensionFilter("Alle Dateien (*.*)", "*.*"));
|
new FileChooser.ExtensionFilter("Alle Dateien (*.*)", "*.*"));
|
||||||
@@ -1401,21 +1401,32 @@ public final class GuiConfigurationEditorWorkspace {
|
|||||||
promptField.setText(picked);
|
promptField.setText(picked);
|
||||||
updateValues(editorState.values().withPromptTemplateFile(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(
|
TextField lockField = boundTextField(
|
||||||
editorState.values().runtimeLockFile(),
|
editorState.values().runtimeLockFile(),
|
||||||
val -> updateValues(editorState.values().withRuntimeLockFile(val)));
|
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(
|
TextField logDirField = boundTextField(
|
||||||
editorState.values().logDirectory(),
|
editorState.values().logDirectory(),
|
||||||
val -> updateValues(editorState.values().withLogDirectory(val)));
|
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;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2518,6 +2529,87 @@ public final class GuiConfigurationEditorWorkspace {
|
|||||||
return slot;
|
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
|
* 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.
|
* row with the label and text field, plus an optional error-slot label directly beneath.
|
||||||
|
|||||||
Reference in New Issue
Block a user