Korrigiere Reihenfolge in resetToFitView für zuverlässige Zentrierung

Zwei zusammenwirkende Ursachen für die linksbündige Anzeige nach Zoom-Reset:

1. Die Property-Bindungen wurden vor setFitToWidth(true) gesetzt. Zu diesem
   Zeitpunkt sizet der viewStack noch nach der zoom-großen ImageView, sodass
   die Bindungen die imageView an die Zoom-Breite gekoppelt haben statt an
   die Viewport-Breite.

2. Verbleibende H/V-Werte aus Pan-/Zoom-Modus (insbesondere hvalue=0.0 nach
   Pan zum linken Rand) wurden nicht zurückgesetzt. Bei minimalsten
   Rounding-/Border-Differenzen wirkt hvalue auch im fit-aktiven Modus und
   richtet den Content links bündig aus.

Fix: setFitToWidth/Height(true) sofort; Bindings und setHvalue(0.5)/
setVvalue(0.5) im Platform.runLater nach abgeschlossenem Layout-Pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 15:26:31 +02:00
parent a8d8a4a3c1
commit 3fb511601c
@@ -743,13 +743,20 @@ public final class PdfPreviewPane {
* Setzt Zoom, Akkumulator und Pan-Zustand zurück und reaktiviert den Fit-to-View-Modus. * Setzt Zoom, Akkumulator und Pan-Zustand zurück und reaktiviert den Fit-to-View-Modus.
* Wird beim Laden einer neuen Datei und beim Leeren der Komponente aufgerufen. * Wird beim Laden einer neuen Datei und beim Leeren der Komponente aufgerufen.
* *
* <p>Die Property-Bindungen von {@code fitWidth}/{@code fitHeight} an * <p>Reihenfolge der Aktionen ist kritisch:
* {@code viewStack.widthProperty()}/{@code viewStack.heightProperty()} werden * <ol>
* wiederhergestellt damit ist der ImageView-Zustand identisch zum initialen * <li>{@code setFitToWidth(true)} und {@code setFitToHeight(true)} sofort,
* Zustand aus dem Konstruktor. Der {@link StackPane} zentriert den ImageView * damit der nächste Layout-Pass den {@code viewStack} auf Viewport-Größe
* dann automatisch über die zuvor gesetzte {@code Pos.CENTER}-Ausrichtung; * zurückrechnet.</li>
* eine explizite H/V-Wert-Manipulation am ScrollPane ist im fit-Modus * <li>Property-Bindungen und H/V-Reset im {@code Platform.runLater}, damit
* wirkungslos und daher nicht nötig. * sie auf die bereits zurückgerechneten {@code viewStack}-Dimensionen
* wirken und nicht auf die noch zoom-große Breite.</li>
* </ol>
* Ohne diese Reihenfolge würden die Bindungen die imageView kurz an die
* Zoom-Größe koppeln, und ein verbleibender H/V-Wert aus dem Pan-/Zoom-Modus
* (z. B. {@code hvalue=0.0} nach Pan zum linken Rand) würde die PDF wegen
* kleinster Rounding-/Border-Differenzen links/oben bündig statt zentriert
* anzeigen, obwohl der ScrollPane fit-aktiv ist.
*/ */
private void resetToFitView() { private void resetToFitView() {
zoomLevel = 1.0; zoomLevel = 1.0;
@@ -760,10 +767,19 @@ public final class PdfPreviewPane {
panStartY = -1; panStartY = -1;
viewStack.setCursor(null); viewStack.setCursor(null);
if (!scrollPane.isFitToWidth()) { if (!scrollPane.isFitToWidth()) {
imageView.fitWidthProperty().bind(viewStack.widthProperty()); // 1. ScrollPane in Fit-Modus schalten, damit der nächste Layout-Pass
imageView.fitHeightProperty().bind(viewStack.heightProperty()); // den viewStack auf Viewport-Größe zurückrechnet
scrollPane.setFitToWidth(true); scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true); scrollPane.setFitToHeight(true);
// 2./3. Bindings und H/V-Reset erst nach abgeschlossenem Layout-Pass,
// damit sie auf die zurückgerechneten Dimensionen wirken und
// Rounding-Reste aus Pan/Zoom die Zentrierung nicht verschieben
Platform.runLater(() -> {
imageView.fitWidthProperty().bind(viewStack.widthProperty());
imageView.fitHeightProperty().bind(viewStack.heightProperty());
scrollPane.setHvalue(0.5);
scrollPane.setVvalue(0.5);
});
} }
} }