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 2329fec..2b916a6 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 @@ -40,6 +40,7 @@ import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiProviderApiKeyState; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiProviderConfigurationState; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiVisibleProviderSection; import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily; +import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerStatus; import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApiKeyDescriptor; import de.gecheckt.pdf.umbenenner.application.validation.editor.ApiKeyResolutionPort; import de.gecheckt.pdf.umbenenner.application.validation.editor.EditorConfigurationValidator; @@ -1082,6 +1083,41 @@ public final class GuiConfigurationEditorWorkspace { return batchRunTab.runningProperty(); } + /** + * Leitet einen aktuellen Scheduler-Status-Snapshot an alle betroffenen Tabs weiter. + *
+ * Wird von der zentralen Status-Refresh-Timeline (1 Hz) auf dem JavaFX Application + * Thread aufgerufen. Die Methode delegiert an: + *
+ * Muss auf dem JavaFX Application Thread aufgerufen werden. Wird von + * {@link #onSchedulerStatusRefresh} und der Status-Refresh-Timeline (1 Hz) indirekt + * aufgerufen. + *
+ * Die konkrete Reaktion (z. B. Banner-Anzeige und Speichern-Button-Sperre während ein + * Scheduler-Lauf aktiv ist) wird in einem späteren Implementierungsschritt ergänzt. + * + * @param status aktueller Scheduler-Status; darf nicht {@code null} sein + */ + public void updateLockState(SchedulerStatus status) { + // Stub – Implementierung folgt in späterem Schritt + } + /** * Behandelt die Aktion „Datenbank → Neue Datenbank anlegen…". *
diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStatusRefreshTimeline.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStatusRefreshTimeline.java new file mode 100644 index 0000000..ff5eceb --- /dev/null +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStatusRefreshTimeline.java @@ -0,0 +1,67 @@ +package de.gecheckt.pdf.umbenenner.adapter.in.gui; + +import java.util.Objects; +import java.util.Optional; + +import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerControlUseCase; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.util.Duration; + +/** + * Zentrale Status-Refresh-Timeline für die GUI. + *
+ * Startet eine JavaFX-{@link Timeline}, die im Sekundentakt einen Callback aufruft. + * Der Callback liest den aktuellen Scheduler-Status und aktualisiert alle betroffenen + * Tabs (Batch-Tab, Konfig-Tab, Scheduler-Tab) auf dem JavaFX Application Thread. + *
+ * Die Timeline wird beim Aufbau der Haupt-GUI gestartet und beim Beenden der + * Anwendung gestoppt. Sie läuft unabhängig davon, welcher Tab gerade sichtbar ist. + *
+ * Wenn kein {@link SchedulerControlUseCase} vorhanden ist, wird der Callback trotzdem + * aufgerufen – der Aufrufer entscheidet, wie er das leere Optional behandelt. + */ +public final class GuiStatusRefreshTimeline { + + private final Timeline timeline; + + /** + * Erzeugt eine neue Status-Refresh-Timeline. + *
+ * Die Timeline ist nach der Konstruktion noch nicht aktiv; {@link #start()} muss
+ * explizit aufgerufen werden.
+ *
+ * @param schedulerControlUseCase optionaler Scheduler-Control-Use-Case;
+ * {@code null} wird als leer behandelt
+ * @param onRefresh Callback der bei jedem Tick auf dem JavaFX Application
+ * Thread aufgerufen wird; darf nicht {@code null} sein
+ */
+ public GuiStatusRefreshTimeline(
+ Optional
+ * Muss auf dem JavaFX Application Thread aufgerufen werden.
+ * Mehrfache Aufrufe sind unschädlich.
+ */
+ public void start() {
+ timeline.play();
+ }
+
+ /**
+ * Stoppt die Status-Refresh-Timeline.
+ *
+ * Muss auf dem JavaFX Application Thread aufgerufen werden.
+ * Mehrfache Aufrufe sind unschädlich.
+ */
+ public void stop() {
+ timeline.stop();
+ }
+}
diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java
index effa84b..6037816 100644
--- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java
+++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/PdfUmbenennerGuiApplication.java
@@ -3,6 +3,7 @@ package de.gecheckt.pdf.umbenenner.adapter.in.gui;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerStatus;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
@@ -29,6 +30,10 @@ import javafx.stage.WindowEvent;
*
* Beim Schließen des Fensters wird die Anwendung in den Windows System-Tray minimiert.
* Über das Tray-Kontextmenü kann das Fenster wieder geöffnet oder die Anwendung beendet werden.
+ *
+ * Nach dem Anzeigen des Hauptfensters startet eine zentrale {@link GuiStatusRefreshTimeline}
+ * (1 Hz), die den aktuellen Scheduler-Status liest und alle betroffenen Tabs aktualisiert.
+ * Die Timeline wird beim Beenden der Anwendung gestoppt.
*/
public class PdfUmbenennerGuiApplication extends Application {
@@ -37,6 +42,9 @@ public class PdfUmbenennerGuiApplication extends Application {
private static final double DEFAULT_HEIGHT = 800;
private SystemTrayManager trayManager;
+ private GuiConfigurationEditorWorkspace workspace;
+ private GuiStartupContext guiStartupContext;
+ private GuiStatusRefreshTimeline refreshTimeline;
/**
* Creates a new instance of the JavaFX application.
@@ -52,6 +60,8 @@ public class PdfUmbenennerGuiApplication extends Application {
* Wires the workspace title-update listener to the stage title so any dirty-state change
* causes an immediate window-title refresh. Installs the close-request handler that
* guards unsaved changes and minimizes the window to the system tray instead of closing.
+ *
+ * Startet nach dem Anzeigen des Fensters die zentrale Status-Refresh-Timeline.
*
* @param primaryStage the primary stage provided by the JavaFX runtime; never {@code null}
*/
@@ -67,14 +77,14 @@ public class PdfUmbenennerGuiApplication extends Application {
new Image(getClass().getResourceAsStream("/icons/Icon128.png"))
);
- GuiStartupContext startupContext = GuiStartupContextHolder.currentOrBlank();
- GuiConfigurationEditorWorkspace workspace = new GuiConfigurationEditorWorkspace(startupContext);
+ guiStartupContext = GuiStartupContextHolder.currentOrBlank();
+ workspace = new GuiConfigurationEditorWorkspace(guiStartupContext);
// Wire the title-update listener so the stage title stays in sync with the dirty state.
workspace.titleUpdateListener = primaryStage::setTitle;
// Statuszeile anlegen und mit dem Workspace verdrahten
- GuiStatusBar statusBar = new GuiStatusBar(startupContext.applicationVersion());
+ GuiStatusBar statusBar = new GuiStatusBar(guiStartupContext.applicationVersion());
workspace.statusBarStateListener = statusBar::applyEditorState;
// Menüleiste mit Datenbank-Menü („Neue Datenbank anlegen…")
@@ -106,22 +116,45 @@ public class PdfUmbenennerGuiApplication extends Application {
// Versuche, die zuletzt geladene Konfigurationsdatei automatisch zu laden.
workspace.autoLoadLastConfiguration();
+ // Zentrale Status-Refresh-Timeline starten (1 Hz)
+ refreshTimeline = new GuiStatusRefreshTimeline(
+ guiStartupContext.schedulerControlUseCase(),
+ this::refreshAllTabStates);
+ refreshTimeline.start();
+
LOG.info("GUI: Hauptfenster erfolgreich angezeigt.");
}
/**
* Called by the JavaFX runtime when the application is stopping.
*
- * Entfernt das System-Tray-Icon und loggt das Beenden.
+ * Stoppt die Status-Refresh-Timeline, entfernt das System-Tray-Icon und loggt das Beenden.
*/
@Override
public void stop() {
LOG.info("GUI: JavaFX-Anwendung wird beendet.");
+ if (refreshTimeline != null) {
+ refreshTimeline.stop();
+ }
if (trayManager != null) {
trayManager.remove();
}
}
+ /**
+ * Liest den aktuellen Scheduler-Status und aktualisiert alle betroffenen Tabs.
+ *
+ * Wird von der {@link GuiStatusRefreshTimeline} im Sekundentakt auf dem JavaFX
+ * Application Thread aufgerufen. Wenn kein {@link SchedulerControlUseCase} vorhanden
+ * ist, wird der Aufruf ohne Fehler übersprungen.
+ */
+ private void refreshAllTabStates() {
+ guiStartupContext.schedulerControlUseCase().ifPresent(uc -> {
+ SchedulerStatus status = uc.getStatus();
+ workspace.onSchedulerStatusRefresh(status);
+ });
+ }
+
/**
* Baut die Menüleiste für das Hauptfenster auf.
*
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 4cf4695..d9c96d4 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
@@ -41,6 +41,7 @@ import de.gecheckt.pdf.umbenenner.application.port.in.ManualFileRenameSourceFile
import de.gecheckt.pdf.umbenenner.application.port.in.ManualFileRenameSuccess;
import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult;
import de.gecheckt.pdf.umbenenner.application.port.in.RunSummary;
+import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerStatus;
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockPort;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;
import de.gecheckt.pdf.umbenenner.domain.model.RunId;
@@ -492,6 +493,21 @@ public final class GuiBatchRunTab {
return askDiscardFilenameChanges();
}
+ /**
+ * Aktualisiert den Tab-Zustand anhand des aktuellen Scheduler-Status.
+ *
+ * Muss auf dem JavaFX Application Thread aufgerufen werden. Wird von der
+ * zentralen Status-Refresh-Timeline (1 Hz) aufgerufen.
+ *
+ * Die konkrete Reaktion (z. B. Deaktivierung des Starten-Buttons während ein
+ * Scheduler-Lauf aktiv ist) wird in einem späteren Implementierungsschritt ergänzt.
+ *
+ * @param status aktueller Scheduler-Status; darf nicht {@code null} sein
+ */
+ public void updateSchedulerState(SchedulerStatus status) {
+ // Stub – Implementierung folgt in späterem Schritt
+ }
+
// -------------------------------------------------------------------------
// Paket-private Accessor für Tests
// -------------------------------------------------------------------------