diff --git a/app/src/main/kotlin/net/metacircular/engpad/ui/editor/PadCanvasView.kt b/app/src/main/kotlin/net/metacircular/engpad/ui/editor/PadCanvasView.kt index d80d638..fc10016 100644 --- a/app/src/main/kotlin/net/metacircular/engpad/ui/editor/PadCanvasView.kt +++ b/app/src/main/kotlin/net/metacircular/engpad/ui/editor/PadCanvasView.kt @@ -93,7 +93,7 @@ class PadCanvasView(context: Context) : View(context) { object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { val newZoom = (zoom * detector.scaleFactor) - .coerceIn(CanvasState.MIN_ZOOM, CanvasState.MAX_ZOOM) + .coerceIn(minZoom(), CanvasState.MAX_ZOOM) if (newZoom != zoom) { val focusX = detector.focusX val focusY = detector.focusY @@ -639,6 +639,23 @@ class PadCanvasView(context: Context) : View(context) { // --- Coordinate transforms --- + /** + * Minimum zoom so the page always fills the viewport. + * At fitScale (zoom=1), the page fills the screen width. + * We need both dimensions covered, so min zoom = max(1, viewH/(pageH*fitScale)). + */ + private fun minZoom(): Float { + val pageW = canvasState.pageSize.widthPt.toFloat() + val pageH = canvasState.pageSize.heightPt.toFloat() + val viewW = width.toFloat().coerceAtLeast(1f) + val viewH = height.toFloat().coerceAtLeast(1f) + val fitScale = viewW / pageW + // Page must fill both dimensions + val minForWidth = 1f // zoom=1 means page width = view width + val minForHeight = viewH / (pageH * fitScale) + return maxOf(minForWidth, minForHeight) + } + private fun rebuildViewMatrix() { viewMatrix.reset() val pageW = canvasState.pageSize.widthPt.toFloat()