Zentriere PDF-Vorschau via viewStack-Mindestgröße statt Timing-Hacks
Korrekte Ursachenanalyse: Im Zoom-Modus schrumpft der viewStack auf Inhalts-Größe (ImageView). Ist der Inhalt kleiner als der Viewport, positioniert ScrollPane den viewStack links/oben – setHvalue(0.5) ist wirkungslos, weil nichts zu scrollen ist. Alle bisherigen runLater/ ChangeListener/AnimationTimer-Ansätze haben am falschen Hebel gedreht. Korrekter Fix: viewportBoundsProperty-Listener im Konstruktor zwingt viewStack auf mindestens Viewport-Größe. Pos.CENTER zentriert dann die ImageView automatisch, wenn sie kleiner ist; bei größerem Inhalt bleibt die Mindestgröße wirkungslos und der ScrollPane scrollt normal. Ersatzlos entfernt: AnimationTimer-Block in applyZoom (wasInFitMode- Zweig), Folge-Schritt-runLater (else-Zweig), setHvalue(0.5)/setVvalue(0.5) in resetToFitView. Bindings in resetToFitView bleiben unverändert. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+15
-33
@@ -17,7 +17,6 @@ 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.embed.swing.SwingFXUtils;
|
||||
import javafx.geometry.Bounds;
|
||||
@@ -244,6 +243,15 @@ public final class PdfPreviewPane {
|
||||
viewStack.addEventHandler(MouseEvent.MOUSE_PRESSED, this::onPanMousePressed);
|
||||
viewStack.addEventHandler(MouseEvent.MOUSE_DRAGGED, this::onPanMouseDragged);
|
||||
viewStack.addEventHandler(MouseEvent.MOUSE_RELEASED, this::onPanMouseReleased);
|
||||
// viewStack ist immer mindestens so groß wie der Viewport. Ist der Inhalt
|
||||
// (ImageView) kleiner als der Viewport, sorgt diese Mindestgröße zusammen
|
||||
// mit StackPane.Pos.CENTER dafür, dass die ImageView automatisch zentriert
|
||||
// wird – ohne manuelle setHvalue/setVvalue-Eingriffe. Ist der Inhalt größer,
|
||||
// bleibt die Mindestgröße wirkungslos und der ScrollPane scrollt normal.
|
||||
scrollPane.viewportBoundsProperty().addListener((obs, old, bounds) -> {
|
||||
viewStack.setMinWidth(bounds.getWidth());
|
||||
viewStack.setMinHeight(bounds.getHeight());
|
||||
});
|
||||
|
||||
prevButton.setId("pdf-preview-prev-button");
|
||||
prevButton.setOnAction(e -> navigateToPreviousPage());
|
||||
@@ -726,33 +734,10 @@ public final class PdfPreviewPane {
|
||||
zoomLevel = effective;
|
||||
imageView.setFitWidth(naturalViewportWidth * zoomLevel);
|
||||
imageView.setFitHeight(0);
|
||||
|
||||
if (wasInFitMode) {
|
||||
// Erster Zoom-Schritt nach Verlassen des Fit-Modus: setFitToWidth(false)
|
||||
// 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);
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
// Folge-Schritte: aktuelle Scroll-Position bewahren
|
||||
double hval = scrollPane.getHvalue();
|
||||
double vval = scrollPane.getVvalue();
|
||||
Platform.runLater(() -> {
|
||||
scrollPane.layout();
|
||||
scrollPane.setHvalue(hval);
|
||||
scrollPane.setVvalue(vval);
|
||||
});
|
||||
}
|
||||
// Keine manuellen setHvalue/setVvalue-Eingriffe nötig: viewStack hat
|
||||
// dank des viewportBoundsProperty-Listeners im Konstruktor mindestens
|
||||
// Viewport-Größe, und Pos.CENTER sorgt für automatische Zentrierung,
|
||||
// wenn der Inhalt kleiner als der Viewport ist.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -787,14 +772,11 @@ public final class PdfPreviewPane {
|
||||
// den viewStack auf Viewport-Größe zurückrechnet
|
||||
scrollPane.setFitToWidth(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
|
||||
// 2. Bindings erst nach abgeschlossenem Layout-Pass, damit sie auf
|
||||
// die zurückgerechneten viewStack-Dimensionen wirken
|
||||
Platform.runLater(() -> {
|
||||
imageView.fitWidthProperty().bind(viewStack.widthProperty());
|
||||
imageView.fitHeightProperty().bind(viewStack.heightProperty());
|
||||
scrollPane.setHvalue(0.5);
|
||||
scrollPane.setVvalue(0.5);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user