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 ae294a5..83244f3 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 @@ -1631,6 +1631,7 @@ public final class GuiConfigurationEditorWorkspace { Button reloadModelsButton = new Button("Modelle neu laden"); reloadModelsButton.setId("modelle-neu-laden-button"); reloadModelsButton.setOnAction(event -> triggerModelRetrievalForCurrentProvider(providerComboBox)); + applyTooltip(reloadModelsButton, GuiTooltipTexts.PROVIDER_MODELLE_NEU_LADEN); HBox comboRow = new HBox(8, providerComboBox, reloadModelsButton); comboRow.setAlignment(Pos.CENTER_LEFT); @@ -2156,6 +2157,7 @@ public final class GuiConfigurationEditorWorkspace { card.getChildren().add(messagesListView); clearMessagesButton.setOnAction(e -> clearMessages()); + applyTooltip(clearMessagesButton, GuiTooltipTexts.TOOLBAR_MELDUNGEN_LEEREN); HBox clearButtonRow = new HBox(clearMessagesButton); clearButtonRow.setAlignment(Pos.CENTER_LEFT); card.getChildren().add(clearButtonRow); @@ -2801,6 +2803,7 @@ public final class GuiConfigurationEditorWorkspace { Button pickButton1 = new Button("…"); pickButton1.setOnAction(e -> onPick1.run()); pickButton1.setMinWidth(32); + applyTooltip(pickButton1, GuiTooltipTexts.PFADE_BROWSER_BUTTON); HBox fieldBox1 = new HBox(4, field1, pickButton1); HBox.setHgrow(field1, Priority.ALWAYS); fieldBox1.setAlignment(Pos.CENTER_LEFT); @@ -2828,6 +2831,7 @@ public final class GuiConfigurationEditorWorkspace { Button pickButton2 = new Button("…"); pickButton2.setOnAction(e -> onPick2.run()); pickButton2.setMinWidth(32); + applyTooltip(pickButton2, GuiTooltipTexts.PFADE_BROWSER_BUTTON); HBox fieldBox2 = new HBox(4, field2, pickButton2); HBox.setHgrow(field2, Priority.ALWAYS); fieldBox2.setAlignment(Pos.CENTER_LEFT); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java index de51bfe..876586d 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java @@ -8,6 +8,7 @@ import java.util.function.Function; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts; import de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingFailure; import de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingSuccess; import de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult; @@ -225,6 +226,7 @@ public class GuiPromptEditorTab { textArea.setWrapText(true); textArea.setFont(Font.font("Monospace", 13)); textArea.setPrefRowCount(20); + textArea.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_TEXTAREA)); VBox.setVgrow(textArea, Priority.ALWAYS); // Dirty-State-Tracking diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java index 1e15b52..7fa9cbb 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java @@ -105,6 +105,134 @@ public final class GuiTooltipTexts { public static final String DATEINAME_ZURUECKSETZEN = "Stellt den KI-generierten Namen wieder her, ohne zu speichern."; + // ------------------------------------------------------------------------- + // Verarbeitungslauf-Tab – Laufsteuerung und Tabelle + // ------------------------------------------------------------------------- + + /** Tooltip für den Button „Starten". */ + public static final String BATCHRUN_STARTEN = + "Verarbeitungslauf starten: alle ausstehenden PDF-Dateien aus dem Quellordner verarbeiten."; + + /** Tooltip für den Button „Abbrechen". */ + public static final String BATCHRUN_ABBRECHEN = + "Laufenden Verarbeitungslauf abbrechen. Bereits abgeschlossene Dateien bleiben gespeichert."; + + /** Tooltip für den Button „Erneut verarbeiten". */ + public static final String BATCHRUN_ERNEUT_VERARBEITEN = + "Markierte Einträge erneut zur Verarbeitung freigeben (setzt Status auf READY_FOR_AI)."; + + /** Tooltip für den Button „Status zurücksetzen" im Verarbeitungslauf-Tab. */ + public static final String BATCHRUN_STATUS_ZURUECKSETZEN = + "Status der markierten Einträge zurücksetzen, damit sie beim nächsten Lauf verarbeitet werden."; + + /** Tooltip für die Master-Checkbox im Tabellenkopf des Verarbeitungslauf-Tabs. */ + public static final String BATCHRUN_MASTER_CHECKBOX = + "Alle sichtbaren Einträge markieren oder Markierung aufheben."; + + /** Tooltip für den Meldungsbereich im Verarbeitungslauf-Tab. */ + public static final String BATCHRUN_MESSAGE_AREA = + "Statusmeldungen und Fortschrittsinformationen des aktuellen Verarbeitungslaufs."; + + /** Tooltip für Spalte „Status" in der Verarbeitungslauf-Tabelle. */ + public static final String BATCHRUN_COL_STATUS = + "Verarbeitungsergebnis: Erfolg, Fehler oder übersprungen."; + + /** Tooltip für Spalte „Originaldateiname" in der Verarbeitungslauf-Tabelle. */ + public static final String BATCHRUN_COL_ORIGINALDATEINAME = + "Ursprünglicher Dateiname der verarbeiteten PDF-Datei."; + + /** Tooltip für Spalte „Neuer Dateiname" in der Verarbeitungslauf-Tabelle. */ + public static final String BATCHRUN_COL_NEUER_DATEINAME = + "Von der KI vorgeschlagener, normierter Dateiname."; + + /** Tooltip für Spalte „Datum" in der Verarbeitungslauf-Tabelle. */ + public static final String BATCHRUN_COL_DATUM = + "Datum des Dokuments laut KI-Analyse."; + + /** Tooltip für Spalte „Dauer" in der Verarbeitungslauf-Tabelle. */ + public static final String BATCHRUN_COL_DAUER = + "Verarbeitungsdauer für diese Datei."; + + // ------------------------------------------------------------------------- + // Verlauf-Tab – Detailbereich + // ------------------------------------------------------------------------- + + /** Tooltip für den KI-Begründungs-Bereich im Verlauf-Tab. */ + public static final String VERLAUF_REASONING_AREA = + "KI-Begründung des ausgewählten Verarbeitungsversuchs."; + + /** Tooltip für den Fehlerursachen-Bereich im Verlauf-Tab. */ + public static final String VERLAUF_FAILURE_AREA = + "Fehlermeldung des letzten Fehler-Versuchs für dieses Dokument."; + + /** Tooltip für Spalte „Status" in der Übersichtstabelle des Verlauf-Tabs. */ + public static final String VERLAUF_COL_STATUS = + "Aktueller Gesamtstatus des Dokuments."; + + /** Tooltip für Spalte „Quelldatei" in der Übersichtstabelle des Verlauf-Tabs. */ + public static final String VERLAUF_COL_QUELLDATEI = + "Ursprünglicher Dateiname der PDF-Quelldatei."; + + /** Tooltip für Spalte „Zieldatei" in der Übersichtstabelle des Verlauf-Tabs. */ + public static final String VERLAUF_COL_ZIELDATEI = + "Vom System erzeugter, normierter Dateiname im Zielordner."; + + /** Tooltip für Spalte „Letzter Versuch" in der Übersichtstabelle des Verlauf-Tabs. */ + public static final String VERLAUF_COL_LETZTER_VERSUCH = + "Zeitpunkt des zuletzt abgeschlossenen Verarbeitungsversuchs."; + + /** Tooltip für Spalte „Versuche" in der Übersichtstabelle des Verlauf-Tabs. */ + public static final String VERLAUF_COL_VERSUCHE = + "Gesamtanzahl der Verarbeitungsversuche für dieses Dokument."; + + /** Tooltip für Spalte „#" in der Versuche-Tabelle des Verlauf-Tabs. */ + public static final String VERLAUF_VERSUCHE_COL_NR = + "Laufende Nummer des Verarbeitungsversuchs."; + + /** Tooltip für Spalte „Datum" in der Versuche-Tabelle des Verlauf-Tabs. */ + public static final String VERLAUF_VERSUCHE_COL_DATUM = + "Endzeitpunkt dieses Verarbeitungsversuchs."; + + /** Tooltip für Spalte „Status" in der Versuche-Tabelle des Verlauf-Tabs. */ + public static final String VERLAUF_VERSUCHE_COL_STATUS = + "Ergebnis dieses Verarbeitungsversuchs."; + + /** Tooltip für Spalte „Provider" in der Versuche-Tabelle des Verlauf-Tabs. */ + public static final String VERLAUF_VERSUCHE_COL_PROVIDER = + "KI-Provider, der für diesen Versuch verwendet wurde."; + + /** Tooltip für Spalte „Modell" in der Versuche-Tabelle des Verlauf-Tabs. */ + public static final String VERLAUF_VERSUCHE_COL_MODELL = + "Konkretes Sprachmodell, das für diesen Versuch verwendet wurde."; + + /** Tooltip für Spalte „Vorgeschlagener Name" in der Versuche-Tabelle des Verlauf-Tabs. */ + public static final String VERLAUF_VERSUCHE_COL_VORGESCHLAGENER_NAME = + "Vom System erzeugter Zieldateiname für diesen Versuch."; + + // ------------------------------------------------------------------------- + // Konfigurations-Tab – Meldungsbereich und Modell-Neu-Laden + // ------------------------------------------------------------------------- + + /** Tooltip für den Button „Meldungen leeren". */ + public static final String TOOLBAR_MELDUNGEN_LEEREN = + "Alle Meldungen im Meldungsbereich entfernen."; + + /** Tooltip für den Button „Modelle neu laden". */ + public static final String PROVIDER_MODELLE_NEU_LADEN = + "Verfügbare Modelle vom konfigurierten Provider neu abrufen."; + + /** Tooltip für den Ordner-/Datei-Browser-Button. */ + public static final String PFADE_BROWSER_BUTTON = + "Ordner oder Datei über den Datei-Dialog auswählen."; + + // ------------------------------------------------------------------------- + // Prompt-Tab – Textbereich + // ------------------------------------------------------------------------- + + /** Tooltip für den Prompt-Textbereich im Prompt-Editor-Tab. */ + public static final String PROMPT_TEXTAREA = + "KI-Anweisungstext. Dieser Prompt wird bei jedem Verarbeitungsversuch an das Sprachmodell gesendet."; + /** Nicht instanziierbar – reine Konstantenklasse. */ private GuiTooltipTexts() { throw new UnsupportedOperationException("Nicht instanziierbar"); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java index dc47c96..9269cf2 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java @@ -43,6 +43,7 @@ import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult; import de.gecheckt.pdf.umbenenner.application.port.in.RunSummary; import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint; import de.gecheckt.pdf.umbenenner.domain.model.RunId; +import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts; import javafx.application.Platform; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; @@ -527,9 +528,11 @@ public final class GuiBatchRunTab { // Selektions-Aktions-Buttons unterhalb der Tabelle (linke Spalte) reprocessButton.setId("batch-run-reprocess"); reprocessButton.setOnAction(event -> handleReprocessSelected()); + reprocessButton.setTooltip(new Tooltip(GuiTooltipTexts.BATCHRUN_ERNEUT_VERARBEITEN)); resetStatusButton.setId("batch-run-reset-status"); resetStatusButton.setOnAction(event -> handleResetSelected()); + resetStatusButton.setTooltip(new Tooltip(GuiTooltipTexts.BATCHRUN_STATUS_ZURUECKSETZEN)); HBox selectionButtonBar = new HBox(SECONDARY_SPACING, reprocessButton, resetStatusButton); selectionButtonBar.setAlignment(Pos.CENTER_LEFT); @@ -540,6 +543,7 @@ public final class GuiBatchRunTab { messageArea.setEditable(false); messageArea.setWrapText(true); messageArea.setPrefRowCount(3); + messageArea.setTooltip(new Tooltip(GuiTooltipTexts.BATCHRUN_MESSAGE_AREA)); // Hinweisbereich erst einblenden wenn eine Meldung vorliegt messageArea.setVisible(false); messageArea.setManaged(false); @@ -600,12 +604,14 @@ public final class GuiBatchRunTab { masterCheckBox.setId("batch-run-master-checkbox"); masterCheckBox.setOnAction(e -> handleMasterCheckBoxAction()); + masterCheckBox.setTooltip(new Tooltip(GuiTooltipTexts.BATCHRUN_MASTER_CHECKBOX)); checkboxCol.setGraphic(masterCheckBox); checkboxCol.setCellFactory(col -> new CheckBoxCell()); checkboxCol.setEditable(true); - TableColumn iconCol = new TableColumn<>("Status"); + TableColumn iconCol = new TableColumn<>(); + iconCol.setGraphic(columnHeader("Status", GuiTooltipTexts.BATCHRUN_COL_STATUS)); iconCol.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().statusIcon())); iconCol.setPrefWidth(64); iconCol.setCellFactory(col -> new TableCell() { @@ -636,11 +642,13 @@ public final class GuiBatchRunTab { } }); - TableColumn nameCol = new TableColumn<>("Originaldateiname"); + TableColumn nameCol = new TableColumn<>(); + nameCol.setGraphic(columnHeader("Originaldateiname", GuiTooltipTexts.BATCHRUN_COL_ORIGINALDATEINAME)); nameCol.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().originalFileName())); nameCol.setPrefWidth(280); - TableColumn newNameCol = new TableColumn<>("Neuer Dateiname"); + TableColumn newNameCol = new TableColumn<>(); + newNameCol.setGraphic(columnHeader("Neuer Dateiname", GuiTooltipTexts.BATCHRUN_COL_NEUER_DATEINAME)); newNameCol.setCellValueFactory(data -> { GuiBatchRunResultRow row = data.getValue(); if (row.resetPending()) { @@ -650,14 +658,16 @@ public final class GuiBatchRunTab { }); newNameCol.setPrefWidth(280); - TableColumn dateCol = new TableColumn<>("Datum"); + TableColumn dateCol = new TableColumn<>(); + dateCol.setGraphic(columnHeader("Datum", GuiTooltipTexts.BATCHRUN_COL_DATUM)); dateCol.setCellValueFactory(data -> new SimpleStringProperty( data.getValue().resolvedDate() .map(DateTimeFormatter.ISO_LOCAL_DATE::format) .orElse(EMPTY_CELL_TEXT))); dateCol.setPrefWidth(100); - TableColumn durationCol = new TableColumn<>("Dauer"); + TableColumn durationCol = new TableColumn<>(); + durationCol.setGraphic(columnHeader("Dauer", GuiTooltipTexts.BATCHRUN_COL_DAUER)); durationCol.setCellValueFactory(data -> new SimpleStringProperty( formatDuration(data.getValue().processingDuration()))); durationCol.setPrefWidth(80); @@ -1145,10 +1155,12 @@ public final class GuiBatchRunTab { // Lauf-Steuerungs-Buttons startButton.setId("batch-run-start"); startButton.setOnAction(event -> handleStart()); + startButton.setTooltip(new Tooltip(GuiTooltipTexts.BATCHRUN_STARTEN)); cancelButton.setId("batch-run-cancel"); cancelButton.setOnAction(event -> requestCancellation()); cancelButton.setDisable(true); + cancelButton.setTooltip(new Tooltip(GuiTooltipTexts.BATCHRUN_ABBRECHEN)); HBox runButtonBar = new HBox(SECONDARY_SPACING, startButton, cancelButton); runButtonBar.setAlignment(Pos.CENTER_LEFT); @@ -1439,6 +1451,21 @@ public final class GuiBatchRunTab { // statusColor() wurde zugunsten von ProcessingStatusPresentation.cssColorFor() entfernt. + /** + * Erzeugt ein Label für den Spaltenkopf einer TableColumn mit Tooltip. + * Wird anstelle von {@code column.setText()} verwendet, da TableColumn + * kein direktes {@code setTooltip()} unterstützt. + * + * @param title sichtbarer Spaltentext; darf nicht leer sein + * @param tooltip Tooltip-Text; darf nicht leer sein + * @return ein Label mit gesetztem Tooltip + */ + private static Label columnHeader(String title, String tooltip) { + Label label = new Label(title); + label.setTooltip(new Tooltip(tooltip)); + return label; + } + private static String formatDuration(Duration duration) { double seconds = duration.toMillis() / 1000.0; if (seconds < 10) { diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java index aef10fd..8b5260c 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java @@ -15,6 +15,7 @@ import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts; import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.ProcessingStatusPresentation; import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord; import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingAttempt; @@ -288,7 +289,8 @@ public final class GuiHistoryTab { overviewTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN); // Status-Icon-Spalte - TableColumn statusCol = new TableColumn<>("Status"); + TableColumn statusCol = new TableColumn<>(); + statusCol.setGraphic(columnHeader("Status", GuiTooltipTexts.VERLAUF_COL_STATUS)); statusCol.setCellValueFactory(cell -> new SimpleStringProperty(statusIcon(cell.getValue().overallStatus()))); statusCol.setCellFactory(col -> new TableCell<>() { @@ -310,27 +312,31 @@ public final class GuiHistoryTab { statusCol.setMaxWidth(70); // Quelldateiname - TableColumn sourceCol = new TableColumn<>("Quelldatei"); + TableColumn sourceCol = new TableColumn<>(); + sourceCol.setGraphic(columnHeader("Quelldatei", GuiTooltipTexts.VERLAUF_COL_QUELLDATEI)); sourceCol.setCellValueFactory(cell -> new SimpleStringProperty(cell.getValue().sourceFileName())); sourceCol.setCellFactory(col -> ellipsisCell()); // Zieldateiname - TableColumn targetCol = new TableColumn<>("Zieldatei"); + TableColumn targetCol = new TableColumn<>(); + targetCol.setGraphic(columnHeader("Zieldatei", GuiTooltipTexts.VERLAUF_COL_ZIELDATEI)); targetCol.setCellValueFactory(cell -> new SimpleStringProperty( cell.getValue().targetFileName() != null ? cell.getValue().targetFileName() : "—")); targetCol.setCellFactory(col -> ellipsisCell()); // Letzter Versuch - TableColumn updatedCol = new TableColumn<>("Letzter Versuch"); + TableColumn updatedCol = new TableColumn<>(); + updatedCol.setGraphic(columnHeader("Letzter Versuch", GuiTooltipTexts.VERLAUF_COL_LETZTER_VERSUCH)); updatedCol.setCellValueFactory(cell -> new SimpleStringProperty(formatInstant(cell.getValue().updatedAt()))); updatedCol.setPrefWidth(140); updatedCol.setMaxWidth(160); // Anzahl Versuche - TableColumn countCol = new TableColumn<>("Versuche"); + TableColumn countCol = new TableColumn<>(); + countCol.setGraphic(columnHeader("Versuche", GuiTooltipTexts.VERLAUF_COL_VERSUCHE)); countCol.setCellValueFactory(cell -> new SimpleStringProperty(String.valueOf(cell.getValue().attemptCount()))); countCol.setPrefWidth(70); @@ -368,11 +374,14 @@ public final class GuiHistoryTab { Label failureTitle = new Label("Fehlerursache (letzter Fehler-Versuch)"); failureTitle.setStyle("-fx-font-weight: bold;"); + failureArea.setTooltip(new Tooltip(GuiTooltipTexts.VERLAUF_FAILURE_AREA)); + // KI-Begründung reasoningArea.setEditable(false); reasoningArea.setWrapText(true); reasoningArea.setPrefRowCount(4); reasoningArea.setText(DETAIL_PLACEHOLDER); + reasoningArea.setTooltip(new Tooltip(GuiTooltipTexts.VERLAUF_REASONING_AREA)); Label reasoningTitle = new Label("KI-Begründung (ausgewählter Versuch)"); reasoningTitle.setStyle("-fx-font-weight: bold;"); @@ -400,37 +409,43 @@ public final class GuiHistoryTab { attemptsTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN); attemptsTable.setPrefHeight(150); - TableColumn numCol = new TableColumn<>("#"); + TableColumn numCol = new TableColumn<>(); + numCol.setGraphic(columnHeader("#", GuiTooltipTexts.VERLAUF_VERSUCHE_COL_NR)); numCol.setCellValueFactory(c -> new SimpleStringProperty(String.valueOf(c.getValue().attemptNumber()))); numCol.setPrefWidth(40); numCol.setMaxWidth(50); - TableColumn dateCol = new TableColumn<>("Datum"); + TableColumn dateCol = new TableColumn<>(); + dateCol.setGraphic(columnHeader("Datum", GuiTooltipTexts.VERLAUF_VERSUCHE_COL_DATUM)); dateCol.setCellValueFactory(c -> new SimpleStringProperty(formatInstant(c.getValue().endedAt()))); dateCol.setPrefWidth(130); dateCol.setMaxWidth(150); - TableColumn statusCol = new TableColumn<>("Status"); + TableColumn statusCol = new TableColumn<>(); + statusCol.setGraphic(columnHeader("Status", GuiTooltipTexts.VERLAUF_VERSUCHE_COL_STATUS)); statusCol.setCellValueFactory(c -> new SimpleStringProperty( ProcessingStatusPresentation.displayTextFor(c.getValue().status()))); statusCol.setPrefWidth(160); - TableColumn providerCol = new TableColumn<>("Provider"); + TableColumn providerCol = new TableColumn<>(); + providerCol.setGraphic(columnHeader("Provider", GuiTooltipTexts.VERLAUF_VERSUCHE_COL_PROVIDER)); providerCol.setCellValueFactory(c -> new SimpleStringProperty( c.getValue().aiProvider() != null ? c.getValue().aiProvider() : "—")); providerCol.setPrefWidth(90); - TableColumn modelCol = new TableColumn<>("Modell"); + TableColumn modelCol = new TableColumn<>(); + modelCol.setGraphic(columnHeader("Modell", GuiTooltipTexts.VERLAUF_VERSUCHE_COL_MODELL)); modelCol.setCellValueFactory(c -> new SimpleStringProperty( c.getValue().modelName() != null ? c.getValue().modelName() : "—")); modelCol.setCellFactory(col -> ellipsisCell()); - TableColumn fileNameCol = new TableColumn<>("Vorgeschlagener Name"); + TableColumn fileNameCol = new TableColumn<>(); + fileNameCol.setGraphic(columnHeader("Vorgeschlagener Name", GuiTooltipTexts.VERLAUF_VERSUCHE_COL_VORGESCHLAGENER_NAME)); fileNameCol.setCellValueFactory(c -> new SimpleStringProperty( c.getValue().finalTargetFileName() != null @@ -831,6 +846,21 @@ public final class GuiHistoryTab { }; } + /** + * Erzeugt ein Label für den Spaltenkopf einer TableColumn mit Tooltip. + * Wird anstelle von {@code column.setText()} verwendet, da TableColumn + * kein direktes {@code setTooltip()} unterstützt. + * + * @param title sichtbarer Spaltentext + * @param tooltip Tooltip-Text + * @return ein Label mit gesetztem Tooltip + */ + private static Label columnHeader(String title, String tooltip) { + Label label = new Label(title); + label.setTooltip(new Tooltip(tooltip)); + return label; + } + private static TableCell ellipsisCell() { return new TableCell<>() { @Override