diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java index 1ac5b0a..c59b4a1 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java @@ -17,8 +17,8 @@ import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException; import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.PDFRenderer; +import javafx.animation.AnimationTimer; import javafx.application.Platform; -import javafx.beans.value.ChangeListener; import javafx.embed.swing.SwingFXUtils; import javafx.geometry.Bounds; import javafx.geometry.Insets; @@ -729,24 +729,20 @@ public final class PdfPreviewPane { if (wasInFitMode) { // Erster Zoom-Schritt nach Verlassen des Fit-Modus: setFitToWidth(false) - // löst einen system-bedingten Reset von hvalue auf 0.0 aus. Statt - // diesen Reset per Timing zu umgehen (was nicht zuverlässig ist), - // wird er aktiv abgefangen: Ein einmaliger ChangeListener auf - // hvalueProperty feuert beim Reset, entfernt sich sofort selbst - // (Single-Shot) und postet anschließend die Zentrierung via - // Platform.runLater. So wirkt setHvalue(0.5)/setVvalue(0.5) - // garantiert nach dem Reset – ohne mehrfaches runLater oder - // layout()-Hacks. - @SuppressWarnings("unchecked") - final ChangeListener[] holder = new ChangeListener[1]; - holder[0] = (obs, oldVal, newVal) -> { - scrollPane.hvalueProperty().removeListener(holder[0]); - Platform.runLater(() -> { + // löst Layout-, CSS- und Property-Reset-Passes innerhalb desselben + // JavaFX-Frames aus. Ein AnimationTimer feuert handle() nach allen + // diesen Passes des aktuellen Frames; das ist der einzige Punkt, + // an dem setHvalue(0.5)/setVvalue(0.5) garantiert nicht mehr von + // einem nachträglichen Reset überschrieben werden. stop() im ersten + // handle()-Aufruf macht den Timer zum Single-Shot. + new AnimationTimer() { + @Override + public void handle(long now) { + stop(); scrollPane.setHvalue(0.5); scrollPane.setVvalue(0.5); - }); - }; - scrollPane.hvalueProperty().addListener(holder[0]); + } + }.start(); } else { // Folge-Schritte: aktuelle Scroll-Position bewahren double hval = scrollPane.getHvalue();