Move SSD view touch checks to typed Kotlin

ober

2a2240e5fd90181c175a15315f4814933aa03b87

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index db78b72..02e5f46 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1178,8 +1178,13 @@
                 rotationIs90 rotationIs180 rotationIs270
                 touchMoveExceeded shouldCollectSelectionMatches
                 selectionUsesRawRect selectionHasMatches
-                selectionDragReady
+                selectionDragReady selectionRectTooSmall
+                touchShouldScale touchShouldIgnoreMultiPointer
+                touchSelectionActive touchPanReady touchTapReady
+                touchPointerReady
                 viewDimensionsReady previousViewSizeMissing
+                viewSizeChangeNeedsFit bitmapViewReady
+                viewportMatchesImage
                 motionActionIsDown motionActionIsMove
                 motionActionIsUp motionActionIsUpOrCancel)
         (type Int32)
@@ -1216,10 +1221,42 @@
                                  (hasStart : Bool)
                                  (hasEnd : Bool)) : Bool
           (and selectMode (and hasStart hasEnd)))
+        (def (selectionRectTooSmall (rect : FloatArray) (minimumSize : Float32)) : Bool
+          (and (< (- (float-array-ref rect (int32 2))
+                     (float-array-ref rect (int32 0)))
+                  minimumSize)
+               (< (- (float-array-ref rect (int32 3))
+                     (float-array-ref rect (int32 1)))
+                  minimumSize)))
+        (def (touchShouldScale (selectMode : Bool)) : Bool
+          (not selectMode))
+        (def (touchShouldIgnoreMultiPointer (pointerCount : Int32)) : Bool
+          (> pointerCount (int32 1)))
+        (def (touchSelectionActive (selectMode : Bool)) : Bool
+          selectMode)
+        (def (touchPanReady (selectMode : Bool) (scalerInProgress : Bool)) : Bool
+          (and (not selectMode) (not scalerInProgress)))
+        (def (touchTapReady (selectMode : Bool) (moved : Bool) (isUp : Bool)) : Bool
+          (and (not selectMode) (and (not moved) isUp)))
+        (def (touchPointerReady (pointerActive : Bool) (selectMode : Bool)) : Bool
+          (and pointerActive selectMode))
         (def (viewDimensionsReady (width : Int32) (height : Int32)) : Bool
           (and (> width (int32 0)) (> height (int32 0))))
         (def (previousViewSizeMissing (oldWidth : Int32) (oldHeight : Int32)) : Bool
           (not (viewDimensionsReady oldWidth oldHeight)))
+        (def (viewSizeChangeNeedsFit (oldWidth : Int32)
+                                     (oldHeight : Int32)
+                                     (hasBitmap : Bool)) : Bool
+          (or (previousViewSizeMissing oldWidth oldHeight) (not hasBitmap)))
+        (def (bitmapViewReady (hasBitmap : Bool) (width : Int32) (height : Int32)) : Bool
+          (and hasBitmap (viewDimensionsReady width height)))
+        (def (viewportMatchesImage (viewport : ViewportState)
+                                   (imageWidth : Int32)
+                                   (imageHeight : Int32)
+                                   (rotationDegrees : Int32)) : Bool
+          (and (= (ViewportState-imageWidth viewport) imageWidth)
+               (and (= (ViewportState-imageHeight viewport) imageHeight)
+                    (= (ViewportState-rotationDegrees viewport) rotationDegrees))))
         (def (motionActionMatches (action : Int32) (expected : Int32)) : Bool
           (= action expected))
         (def (motionActionIsDown (action : Int32) (down : Int32)) : Bool
@@ -7656,14 +7693,14 @@
        ""
 	       "    private val scaler = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {"
 	       "        override fun onScale(detector: ScaleGestureDetector): Boolean {"
-	       "            if (selectMode) return false"
+       "            if (touchSelectionActive(selectMode)) return false"
 	       "            zoomAt(detector.focusX, detector.focusY, detector.scaleFactor)"
 	       "            return true"
 	       "        }"
 	       "    })"
        ""
        "    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {"
-       "        if (previousViewSizeMissing(oldw, oldh) || bitmap == null) {"
+       "        if (viewSizeChangeNeedsFit(oldw, oldh, bitmap != null)) {"
        "            fitImage()"
 	       "        } else {"
 	       "            clampOffsets()"
@@ -7689,16 +7726,16 @@
 	       "    }"
 	       ""
 	       "    override fun onTouchEvent(event: MotionEvent): Boolean {"
-	       "        if (!selectMode) {"
+		       "        if (touchShouldScale(selectMode)) {"
 	       "            scaler.onTouchEvent(event)"
 	       "        }"
-	       "        if (event.pointerCount > 1) return true"
+       "        if (touchShouldIgnoreMultiPointer(event.pointerCount)) return true"
        "        if (motionActionIsDown(event.actionMasked, MotionEvent.ACTION_DOWN)) {"
        "                parent.requestDisallowInterceptTouchEvent(true)"
        "                lastX = event.x"
        "                lastY = event.y"
 	       "                moved = false"
-	       "                if (selectMode) {"
+		       "                if (touchSelectionActive(selectMode)) {"
 	       "                    pointerX = event.x"
 	       "                    pointerY = event.y"
 	       "                    pointerActive = true"
@@ -7709,12 +7746,12 @@
        "                val dx = event.x - lastX"
        "                val dy = event.y - lastY"
        "                if (touchMoveExceeded(dx, dy)) moved = true"
-	       "                if (selectMode) {"
+		       "                if (touchSelectionActive(selectMode)) {"
 	       "                    pointerX = event.x"
 	       "                    pointerY = event.y"
 	       "                    pointerActive = true"
 	       "                    dragEnd = selectionPoint(event)"
-	       "                } else if (!scaler.isInProgress) {"
+		       "                } else if (touchPanReady(selectMode, scaler.isInProgress)) {"
 	       "                    offsetX += dx"
 	       "                    offsetY += dy"
 	       "                    clampOffsets()"
@@ -7726,7 +7763,7 @@
        "        } else if (motionActionIsUpOrCancel(event.actionMasked, MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL)) {"
        "                if (selectionDragReady(selectMode, dragStart != null, dragEnd != null)) {"
        "                    finishSelection()"
-       "                } else if (!selectMode && !moved && motionActionIsUp(event.actionMasked, MotionEvent.ACTION_UP)) {"
+       "                } else if (touchTapReady(selectMode, moved, motionActionIsUp(event.actionMasked, MotionEvent.ACTION_UP))) {"
        "                    val point = viewToImage(event.x, event.y)"
        "                    hitGroup(point[0], point[1])?.let {"
        "                        selectedGroupId = it"
@@ -7773,8 +7810,7 @@
        "    fun restoreViewport(viewport: ViewportState?) {"
        "        val img = bitmap ?: return"
        "        if (viewport == null) return"
-       "        if (viewport.imageWidth != img.width || viewport.imageHeight != img.height) return"
-       "        if (viewport.rotationDegrees != rotationDegrees) return"
+       "        if (!viewportMatchesImage(viewport, img.width, img.height, rotationDegrees)) return"
 	       "        scale = viewport.scale"
 	       "        offsetX = viewport.offsetX"
 	       "        offsetY = viewport.offsetY"
@@ -7784,7 +7820,7 @@
 	       "    }"
 	       ""
 	       "    fun centerOnGroup(group: SsdGroup) {"
-	       "        if (bitmap == null || !viewDimensionsReady(width, height)) return"
+	       "        if (!bitmapViewReady(bitmap != null, width, height)) return"
 	       "        scale = focusGroupScale(scale, width.toFloat(), height.toFloat(), group)"
 	       "        updateMatrix()"
 	       "        val groupCenter = groupCenterPoint(group)"
@@ -7817,7 +7853,7 @@
 	       "            onStatus?.invoke(\"Selection ignored: truth/detection is still loading\")"
 	       "            return"
 	       "        }"
-	       "        if ((rect[2] - rect[0]) < 4f && (rect[3] - rect[1]) < 4f) {"
+		       "        if (selectionRectTooSmall(rect, 4f)) {"
 	       "            onStatus?.invoke(\"Selection ignored: drag a larger area\")"
 	       "            return"
 	       "        }"
@@ -7928,7 +7964,7 @@
 	       "    }"
 	       ""
 	       "    private fun drawTouchPointer(canvas: Canvas) {"
-	       "        if (!pointerActive || !selectMode) return"
+		       "        if (!touchPointerReady(pointerActive, selectMode)) return"
 	       "        val anchor = dragEnd ?: dragStart ?: return"
 	       "        val viewAnchor = imageToView(anchor[0], anchor[1])"
 	       "        paint.style = Paint.Style.STROKE"