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.ImageType;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.geometry.Bounds;
|
import javafx.geometry.Bounds;
|
||||||
@@ -244,6 +243,15 @@ public final class PdfPreviewPane {
|
|||||||
viewStack.addEventHandler(MouseEvent.MOUSE_PRESSED, this::onPanMousePressed);
|
viewStack.addEventHandler(MouseEvent.MOUSE_PRESSED, this::onPanMousePressed);
|
||||||
viewStack.addEventHandler(MouseEvent.MOUSE_DRAGGED, this::onPanMouseDragged);
|
viewStack.addEventHandler(MouseEvent.MOUSE_DRAGGED, this::onPanMouseDragged);
|
||||||
viewStack.addEventHandler(MouseEvent.MOUSE_RELEASED, this::onPanMouseReleased);
|
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.setId("pdf-preview-prev-button");
|
||||||
prevButton.setOnAction(e -> navigateToPreviousPage());
|
prevButton.setOnAction(e -> navigateToPreviousPage());
|
||||||
@@ -726,33 +734,10 @@ public final class PdfPreviewPane {
|
|||||||
zoomLevel = effective;
|
zoomLevel = effective;
|
||||||
imageView.setFitWidth(naturalViewportWidth * zoomLevel);
|
imageView.setFitWidth(naturalViewportWidth * zoomLevel);
|
||||||
imageView.setFitHeight(0);
|
imageView.setFitHeight(0);
|
||||||
|
// Keine manuellen setHvalue/setVvalue-Eingriffe nötig: viewStack hat
|
||||||
if (wasInFitMode) {
|
// dank des viewportBoundsProperty-Listeners im Konstruktor mindestens
|
||||||
// Erster Zoom-Schritt nach Verlassen des Fit-Modus: setFitToWidth(false)
|
// Viewport-Größe, und Pos.CENTER sorgt für automatische Zentrierung,
|
||||||
// löst Layout-, CSS- und Property-Reset-Passes innerhalb desselben
|
// wenn der Inhalt kleiner als der Viewport ist.
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -787,14 +772,11 @@ public final class PdfPreviewPane {
|
|||||||
// den viewStack auf Viewport-Größe zurückrechnet
|
// 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,
|
// 2. Bindings erst nach abgeschlossenem Layout-Pass, damit sie auf
|
||||||
// damit sie auf die zurückgerechneten Dimensionen wirken und
|
// die zurückgerechneten viewStack-Dimensionen wirken
|
||||||
// Rounding-Reste aus Pan/Zoom die Zentrierung nicht verschieben
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
imageView.fitWidthProperty().bind(viewStack.widthProperty());
|
imageView.fitWidthProperty().bind(viewStack.widthProperty());
|
||||||
imageView.fitHeightProperty().bind(viewStack.heightProperty());
|
imageView.fitHeightProperty().bind(viewStack.heightProperty());
|
||||||
scrollPane.setHvalue(0.5);
|
|
||||||
scrollPane.setVvalue(0.5);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user