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 javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
@@ -727,20 +728,25 @@ public final class PdfPreviewPane {
imageView.setFitHeight(0);
if (wasInFitMode) {
// Erster Zoom-Schritt nach Verlassen des Fit-Modus: doppelt
// verschachteltes runLater. Das erste runLater stößt den Layout-Pass
// nach setFitToWidth(false) an; das zweite feuert im darauffolgenden
// Pulse, wenn alle Layout-Folgen abgeschlossen sind. Andernfalls
// überschreibt der system-bedingte H/V-Reset auf 0.0 (ausgelöst durch
// setFitToWidth(false)) den setHvalue(0.5)-Aufruf, und die PDF
// springt links/oben bündig statt zentriert zu erscheinen.
Platform.runLater(() -> {
scrollPane.layout();
// 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<Number>[] holder = new ChangeListener[1];
holder[0] = (obs, oldVal, newVal) -> {
scrollPane.hvalueProperty().removeListener(holder[0]);
Platform.runLater(() -> {
scrollPane.setHvalue(0.5);
scrollPane.setVvalue(0.5);
});
});
};
scrollPane.hvalueProperty().addListener(holder[0]);
} else {
// Folge-Schritte: aktuelle Scroll-Position bewahren
double hval = scrollPane.getHvalue();