#85: Verwerfen im Prompt-Tab setzt Dirty-State und laedt Inhalt neu

Nach Bestaetigung des Verwerfen-Dialogs fehlte der Aufruf, der den
Tab-Zustand zuruecksetzt. Neue Methode discardChanges() in
GuiPromptEditorTab setzt loadedContent, dirty und Tab-Titel zurueck;
ist der Tab sichtbar, wird loadPromptAsync() sofort ausgeloest, sonst
greift der bestehende selectedProperty-Listener beim naechsten Oeffnen.
GuiConfigurationEditorWorkspace ruft discardChanges() nach positivem
Bestaedigungsdialog auf. Neuer Smoke-Test verifiziert das Verhalten.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
van Elst, Marcus
2026-05-04 12:31:18 +02:00
parent 3b3e997d13
commit 349ee69a7f
3 changed files with 61 additions and 0 deletions
@@ -1389,6 +1389,8 @@ public final class GuiConfigurationEditorWorkspace {
boolean shouldDiscard = promptEditorTab.confirmDiscardIfDirty(); boolean shouldDiscard = promptEditorTab.confirmDiscardIfDirty();
if (!shouldDiscard) { if (!shouldDiscard) {
Platform.runLater(() -> tabPane.getSelectionModel().select(oldTab)); Platform.runLater(() -> tabPane.getSelectionModel().select(oldTab));
} else {
promptEditorTab.discardChanges();
} }
} }
}); });
@@ -154,6 +154,25 @@ public class GuiPromptEditorTab {
} }
} }
/**
* Verwirft alle ungespeicherten Änderungen und setzt den Tab in den Lade-Bereitschaftszustand.
* <p>
* Setzt Dirty-State und Tab-Titel zurück. Ist der Tab zum Zeitpunkt des Aufrufs sichtbar,
* wird der Prompt-Inhalt sofort neu geladen; andernfalls erfolgt das Laden beim nächsten
* Öffnen des Tabs (gesteuert durch den Tab-Selektions-Listener).
* <p>
* Muss auf dem JavaFX Application Thread aufgerufen werden.
*/
public void discardChanges() {
this.loadedContent = null;
this.dirty = false;
this.tab.setText(TAB_TITLE);
this.saveButton.setDisable(true);
if (tab.isSelected()) {
loadPromptAsync();
}
}
/** /**
* Zeigt einen Bestätigungsdialog, wenn ungespeicherte Änderungen vorhanden sind. * Zeigt einen Bestätigungsdialog, wenn ungespeicherte Änderungen vorhanden sind.
* Gibt {@code true} zurück, wenn die Änderungen verworfen werden dürfen. * Gibt {@code true} zurück, wenn die Änderungen verworfen werden dürfen.
@@ -328,4 +328,44 @@ class GuiPromptEditorTabSmokeTest {
"Tab-Titel muss nach Bearbeitung (resetToDefault) einen Asterisk enthalten; Titel war: " "Tab-Titel muss nach Bearbeitung (resetToDefault) einen Asterisk enthalten; Titel war: "
+ titleRef.get()); + titleRef.get());
} }
@Test
void discardChanges_shouldResetDirtyStateAndTitle() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Throwable> fxError = new AtomicReference<>();
AtomicBoolean dirtyRef = new AtomicBoolean(true);
AtomicReference<String> titleRef = new AtomicReference<>();
Platform.runLater(() -> {
try {
SyncPromptEditorPort port = new SyncPromptEditorPort();
GuiPromptEditorTab editorTab = buildSyncTab(port);
editorTab.loadPromptAsync();
editorTab.resetToDefault();
// Vorbedingung: Dirty-State muss aktiv sein
assertTrue(editorTab.hasDirtyContent(),
"Vorbedingung: Dirty-State muss nach resetToDefault aktiv sein");
// Verwerfen simulieren
editorTab.discardChanges();
dirtyRef.set(editorTab.hasDirtyContent());
titleRef.set(editorTab.tab().getText());
} catch (Throwable t) {
fxError.set(t);
} finally {
latch.countDown();
}
});
assertTrue(latch.await(FX_TIMEOUT_SECONDS, TimeUnit.SECONDS));
if (fxError.get() != null) {
throw new AssertionError("FX-Thread hat eine Ausnahme geworfen", fxError.get());
}
assertFalse(dirtyRef.get(),
"Dirty-State muss nach discardChanges false sein");
assertFalse(titleRef.get().contains("*"),
"Tab-Titel darf nach discardChanges keinen Asterisk enthalten; Titel war: "
+ titleRef.get());
}
} }