Fange JavaFX-Reset von hvalue mit ChangeListener ab statt per Timing

Beim Verlassen des Fit-Modus resettet JavaFX hvalue mehrfach auf 0.0,
auch nach unserem Platform.runLater-Aufruf. Verschachtelte runLater
können diesen Reset nicht zuverlässig überholen.

Lösung: Single-Shot-ChangeListener auf hvalueProperty. Er feuert beim
Reset, entfernt sich selbst und postet erst dann setHvalue(0.5)/
setVvalue(0.5) – garantiert nach dem Reset, ohne Timing-Annahmen.

Folge-Zoom-Schritte (wasInFitMode == false) bleiben unverändert mit
einfachem runLater.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 15:51:35 +02:00
parent b62db18f0c
commit 0651fcb6eb
@@ -18,6 +18,7 @@ import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.PDFRenderer;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.embed.swing.SwingFXUtils; import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds; import javafx.geometry.Bounds;
import javafx.geometry.Insets; import javafx.geometry.Insets;
@@ -727,20 +728,25 @@ public final class PdfPreviewPane {
imageView.setFitHeight(0); imageView.setFitHeight(0);
if (wasInFitMode) { if (wasInFitMode) {
// Erster Zoom-Schritt nach Verlassen des Fit-Modus: doppelt // Erster Zoom-Schritt nach Verlassen des Fit-Modus: setFitToWidth(false)
// verschachteltes runLater. Das erste runLater stößt den Layout-Pass // löst einen system-bedingten Reset von hvalue auf 0.0 aus. Statt
// nach setFitToWidth(false) an; das zweite feuert im darauffolgenden // diesen Reset per Timing zu umgehen (was nicht zuverlässig ist),
// Pulse, wenn alle Layout-Folgen abgeschlossen sind. Andernfalls // wird er aktiv abgefangen: Ein einmaliger ChangeListener auf
// überschreibt der system-bedingte H/V-Reset auf 0.0 (ausgelöst durch // hvalueProperty feuert beim Reset, entfernt sich sofort selbst
// setFitToWidth(false)) den setHvalue(0.5)-Aufruf, und die PDF // (Single-Shot) und postet anschließend die Zentrierung via
// springt links/oben bündig statt zentriert zu erscheinen. // Platform.runLater. So wirkt setHvalue(0.5)/setVvalue(0.5)
Platform.runLater(() -> { // garantiert nach dem Reset ohne mehrfaches runLater oder
scrollPane.layout(); // layout()-Hacks.
@SuppressWarnings("unchecked")
final ChangeListener<Number>[] holder = new ChangeListener[1];
holder[0] = (obs, oldVal, newVal) -> {
scrollPane.hvalueProperty().removeListener(holder[0]);
Platform.runLater(() -> { Platform.runLater(() -> {
scrollPane.setHvalue(0.5); scrollPane.setHvalue(0.5);
scrollPane.setVvalue(0.5); scrollPane.setVvalue(0.5);
}); });
}); };
scrollPane.hvalueProperty().addListener(holder[0]);
} else { } else {
// Folge-Schritte: aktuelle Scroll-Position bewahren // Folge-Schritte: aktuelle Scroll-Position bewahren
double hval = scrollPane.getHvalue(); double hval = scrollPane.getHvalue();