Kalibriere zoomLevel beim Verlassen des Fit-Modus auf visuellen Skalierungsfaktor
Beim ersten Zoom-Schritt sprang die ImageView abrupt von der visuell sichtbaren Breite (durch fitHeight aspekt-erhaltend verkleinert) auf naturalViewportWidth × zoomLevel, weil zoomLevel mit dem Wert 1.0 nicht zur tatsächlich angezeigten Skalierung passte und gleichzeitig setFitHeight(0) die Höhenrestriktion entfernte. applyZoom() initialisiert nun beim Verlassen des Fit-Modus zoomLevel auf currentVisualWidth / naturalImageWidth (= aktueller visueller Skalierungsfaktor) und setzt naturalViewportWidth auf die natürliche Bildbreite. Damit entspricht zoomLevel = 1.0 der pixel-genauen Originaldarstellung. Der vom Caller intendierte Delta-Schritt wird vor der Kalibrierung gesichert und nach der Kalibrierung auf den neuen zoomLevel re-appliziert, damit applyZoom(zoomLevel + 0.10) nicht unverändert auf den kalibrierten Wert feuert. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+46
-11
@@ -126,8 +126,12 @@ public final class PdfPreviewPane {
|
||||
private double zoomAccumulator = 0.0;
|
||||
|
||||
/**
|
||||
* Viewport-Breite zum Zeitpunkt des ersten manuellen Zooms; dient als Referenzbreite
|
||||
* für alle nachfolgenden Zoomstufen. 0.0 bedeutet Fit-to-View-Modus ist aktiv.
|
||||
* Referenzbreite für die manuelle Zoom-Skalierung; gilt
|
||||
* {@code imageView.fitWidth = naturalViewportWidth × zoomLevel} im manuellen
|
||||
* Zoom-Modus. Beim Verlassen des Fit-Modus wird der Wert auf die natürliche
|
||||
* Bildbreite gesetzt, sodass {@code zoomLevel = 1.0} der pixel-genauen
|
||||
* Originalgröße entspricht und {@code zoomLevel} damit gleich dem visuellen
|
||||
* Skalierungsfaktor ist. {@code 0.0} bedeutet Fit-to-View-Modus ist aktiv.
|
||||
*/
|
||||
private double naturalViewportWidth = 0.0;
|
||||
|
||||
@@ -655,12 +659,25 @@ public final class PdfPreviewPane {
|
||||
|
||||
/**
|
||||
* Setzt den Zoomfaktor und verlässt beim ersten Aufruf den Fit-to-View-Modus.
|
||||
* Die Referenzbreite wird einmalig aus dem Viewport übernommen.
|
||||
* <p>
|
||||
* Beim ersten Aufruf (Wechsel aus dem Fit-Modus) wird {@code zoomLevel} auf
|
||||
* den aktuellen visuellen Skalierungsfaktor kalibriert: aktuelle visuelle
|
||||
* Breite der ImageView (mit {@code preserveRatio} bereits aspekt-korrekt
|
||||
* verkleinert) geteilt durch die natürliche Bildbreite. Damit entspricht
|
||||
* {@code zoomLevel = 1.0} der pixel-genauen Originalgröße, und der erste
|
||||
* Zoom-Schritt addiert sich auf den realen Skalierungsfaktor. Ohne diese
|
||||
* Kalibrierung springt die ImageView abrupt auf {@code Viewport-Breite × 1.10},
|
||||
* weil im Fit-Modus die {@code fitHeight}-Bindung das Bild aspekt-erhaltend
|
||||
* deutlich kleiner zwingt als {@code naturalViewportWidth × 1.0} ergibt.
|
||||
* Da der Caller den Delta-Schritt auf dem alten {@code zoomLevel = 1.0}
|
||||
* berechnet hat, wird er nach der Kalibrierung auf den neuen, kalibrierten
|
||||
* {@code zoomLevel} re-appliziert.
|
||||
* <p>
|
||||
* Beim Wechsel aus dem Fit-to-View-Modus wird die Ansicht auf die Bildmitte
|
||||
* zentriert (H/V = 0.5). Bei weiteren Zoom-Schritten bleibt die aktuelle
|
||||
* Scrollposition erhalten. Ein {@code layout()}-Aufruf vor der Positionswiederherstellung
|
||||
* stellt sicher, dass die neuen Inhaltsgrenzen bereits berechnet sind.
|
||||
* Scrollposition erhalten. Ein {@code layout()}-Aufruf vor der
|
||||
* Positionswiederherstellung stellt sicher, dass die neuen Inhaltsgrenzen
|
||||
* bereits berechnet sind.
|
||||
*
|
||||
* @param newZoom gewünschter Zoomfaktor, wird auf [{@link #ZOOM_MIN}, {@link #ZOOM_MAX}] begrenzt
|
||||
*/
|
||||
@@ -669,17 +686,35 @@ public final class PdfPreviewPane {
|
||||
|
||||
boolean wasInFitMode = scrollPane.isFitToWidth();
|
||||
if (wasInFitMode) {
|
||||
Image image = imageView.getImage();
|
||||
if (image == null || image.getWidth() <= 0) {
|
||||
return; // Kein Bild – Zoom-Kalibrierung nicht möglich
|
||||
}
|
||||
double naturalImageWidth = image.getWidth();
|
||||
double currentVisualWidth = imageView.getBoundsInLocal().getWidth();
|
||||
if (currentVisualWidth <= 0) {
|
||||
Bounds viewport = scrollPane.getViewportBounds();
|
||||
double vpWidth = viewport != null ? viewport.getWidth() : viewStack.getWidth();
|
||||
if (vpWidth <= 0) {
|
||||
currentVisualWidth = viewport != null ? viewport.getWidth() : viewStack.getWidth();
|
||||
if (currentVisualWidth <= 0) {
|
||||
return; // Layout noch nicht abgeschlossen
|
||||
}
|
||||
naturalViewportWidth = vpWidth;
|
||||
}
|
||||
|
||||
// Vom Caller intendierten Delta-Schritt vor der Kalibrierung sichern
|
||||
double requestedDelta = newZoom - zoomLevel;
|
||||
|
||||
// zoomLevel auf den aktuellen visuellen Skalierungsfaktor kalibrieren
|
||||
naturalViewportWidth = naturalImageWidth;
|
||||
zoomLevel = currentVisualWidth / naturalImageWidth;
|
||||
|
||||
// effective neu berechnen, weil zoomLevel sich geändert hat
|
||||
effective = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoomLevel + requestedDelta));
|
||||
|
||||
scrollPane.setFitToWidth(false);
|
||||
scrollPane.setFitToHeight(false);
|
||||
imageView.fitWidthProperty().unbind();
|
||||
imageView.fitHeightProperty().unbind();
|
||||
// 32d: Mauszeiger signalisiert Pan-Modus
|
||||
// Mauszeiger signalisiert Pan-Modus
|
||||
viewStack.setCursor(Cursor.OPEN_HAND);
|
||||
}
|
||||
|
||||
@@ -687,7 +722,7 @@ public final class PdfPreviewPane {
|
||||
return;
|
||||
}
|
||||
|
||||
// 32b: Beim ersten Zoom Mitte beibehalten; danach aktuelle Position bewahren
|
||||
// Beim ersten Zoom Mitte beibehalten; danach aktuelle Position bewahren
|
||||
double hval = wasInFitMode ? 0.5 : scrollPane.getHvalue();
|
||||
double vval = wasInFitMode ? 0.5 : scrollPane.getVvalue();
|
||||
|
||||
@@ -695,7 +730,7 @@ public final class PdfPreviewPane {
|
||||
imageView.setFitWidth(naturalViewportWidth * zoomLevel);
|
||||
imageView.setFitHeight(0);
|
||||
|
||||
// 32b: layout() stellt sicher, dass die neuen Inhaltsgrenzen bekannt sind,
|
||||
// layout() stellt sicher, dass die neuen Inhaltsgrenzen bekannt sind,
|
||||
// bevor die Scroll-Werte restauriert werden
|
||||
Platform.runLater(() -> {
|
||||
scrollPane.layout();
|
||||
|
||||
Reference in New Issue
Block a user