Move SSD view drawing checks to typed Kotlin

ober

477b0714ed4ef25552dd102828e0591d3a802beb

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index c69219d..8f5cb56 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1181,10 +1181,11 @@
                 selectionDragReady selectionRectTooSmall
                 touchShouldScale touchShouldIgnoreMultiPointer
                 touchSelectionActive touchPanReady touchTapReady
-                touchPointerReady
+                touchPointerReady touchPointerNotReady
                 viewDimensionsReady previousViewSizeMissing
-                viewSizeChangeNeedsFit bitmapViewReady
-                viewportMatchesImage
+                viewDimensionsMissing viewSizeChangeNeedsFit
+                bitmapViewReady bitmapViewNotReady
+                viewportMatchesImage viewportImageMismatch
                 motionActionIsDown motionActionIsMove
                 motionActionIsUp motionActionIsUpOrCancel)
         (type Int32)
@@ -1240,8 +1241,12 @@
           (and (not selectMode) (and (not moved) isUp)))
         (def (touchPointerReady (pointerActive : Bool) (selectMode : Bool)) : Bool
           (and pointerActive selectMode))
+        (def (touchPointerNotReady (pointerActive : Bool) (selectMode : Bool)) : Bool
+          (not (touchPointerReady pointerActive selectMode)))
         (def (viewDimensionsReady (width : Int32) (height : Int32)) : Bool
           (and (> width (int32 0)) (> height (int32 0))))
+        (def (viewDimensionsMissing (width : Int32) (height : Int32)) : Bool
+          (not (viewDimensionsReady width height)))
         (def (previousViewSizeMissing (oldWidth : Int32) (oldHeight : Int32)) : Bool
           (not (viewDimensionsReady oldWidth oldHeight)))
         (def (viewSizeChangeNeedsFit (oldWidth : Int32)
@@ -1250,6 +1255,8 @@
           (or (previousViewSizeMissing oldWidth oldHeight) (not hasBitmap)))
         (def (bitmapViewReady (hasBitmap : Bool) (width : Int32) (height : Int32)) : Bool
           (and hasBitmap (viewDimensionsReady width height)))
+        (def (bitmapViewNotReady (hasBitmap : Bool) (width : Int32) (height : Int32)) : Bool
+          (not (bitmapViewReady hasBitmap width height)))
         (def (viewportMatchesImage (viewport : ViewportState)
                                    (imageWidth : Int32)
                                    (imageHeight : Int32)
@@ -1257,6 +1264,11 @@
           (and (= (ViewportState-imageWidth viewport) imageWidth)
                (and (= (ViewportState-imageHeight viewport) imageHeight)
                     (= (ViewportState-rotationDegrees viewport) rotationDegrees))))
+        (def (viewportImageMismatch (viewport : ViewportState)
+                                    (imageWidth : Int32)
+                                    (imageHeight : Int32)
+                                    (rotationDegrees : Int32)) : Bool
+          (not (viewportMatchesImage viewport imageWidth imageHeight rotationDegrees)))
         (def (motionActionMatches (action : Int32) (expected : Int32)) : Bool
           (= action expected))
         (def (motionActionIsDown (action : Int32) (down : Int32)) : Bool
@@ -4195,7 +4207,7 @@
                 dialogCountValue editedGroupCountText
                 selectionIgnoredLoadingStatus
                 shouldShowFiringArc shouldFocusFiringArc
-                canvasLabelPresent
+                canvasLabelPresent canvasLabelMissing groupIdSelected
                 shouldCaptureViewport
                 hasReplacementLabel hasResolvedLabel
                 ssdBrowserResumeReady focusChangeShouldShowDropdown
@@ -4667,6 +4679,13 @@
                (string-blank? firingArcText)))
         (def (canvasLabelPresent (label : String)) : Bool
           (not (string-blank? label)))
+        (def (canvasLabelMissing (label : String)) : Bool
+          (not (canvasLabelPresent label)))
+        (def (groupIdSelected (groupId : String)
+                              (selectedGroupId : (Nullable String))) : Bool
+          (if (nullable-null? selectedGroupId)
+            #f
+            (equal? groupId (nullable-get selectedGroupId))))
         (def (shouldCaptureViewport (includeViewport : Bool)) : Bool
           includeViewport)
         (def (pdfLoaded (hasPdf : Bool)) : Bool
@@ -7885,7 +7904,7 @@
        ""
        "    fun fitImage() {"
        "        val img = bitmap ?: return"
-       "        if (!viewDimensionsReady(width, height)) return"
+       "        if (viewDimensionsMissing(width, height)) return"
        "        val displayWidth = rotatedDisplayWidth(rotationDegrees, img.width, img.height)"
        "        val displayHeight = rotatedDisplayHeight(rotationDegrees, img.width, img.height)"
        "        scale = fitImageScale(width.toFloat(), height.toFloat(), displayWidth.toFloat(), displayHeight.toFloat())"
@@ -7911,7 +7930,7 @@
        "    fun restoreViewport(viewport: ViewportState?) {"
        "        val img = bitmap ?: return"
        "        if (viewport == null) return"
-       "        if (!viewportMatchesImage(viewport, img.width, img.height, rotationDegrees)) return"
+       "        if (viewportImageMismatch(viewport, img.width, img.height, rotationDegrees)) return"
 	       "        scale = viewport.scale"
 	       "        offsetX = viewport.offsetX"
 	       "        offsetY = viewport.offsetY"
@@ -7921,7 +7940,7 @@
 	       "    }"
 	       ""
 	       "    fun centerOnGroup(group: SsdGroup) {"
-	       "        if (!bitmapViewReady(bitmap != null, width, height)) return"
+       "        if (bitmapViewNotReady(bitmap != null, width, height)) return"
 	       "        scale = focusGroupScale(scale, width.toFloat(), height.toFloat(), group)"
 	       "        updateMatrix()"
 	       "        val groupCenter = groupCenterPoint(group)"
@@ -7991,7 +8010,7 @@
        "        paint.style = Paint.Style.STROKE"
        "        paint.strokeWidth = groupBaseStrokeWidth(scale)"
        "        for (group in sess.groups) {"
-       "            val selected = group.id == selectedGroupId"
+       "            val selected = groupIdSelected(group.id, selectedGroupId)"
        "            paint.color = groupStrokeColor(selected, group.status)"
        "            paint.strokeWidth = groupStrokeWidth(selected, scale)"
        "            canvas.drawRect(group.bbox[0], group.bbox[1], group.bbox[2], group.bbox[3], paint)"
@@ -8018,9 +8037,9 @@
 	       "    private fun drawLabels(canvas: Canvas) {"
        "        val sess = session ?: return"
        "        for (group in sess.groups) {"
-       "            val selected = group.id == selectedGroupId"
+       "            val selected = groupIdSelected(group.id, selectedGroupId)"
        "            val label = displayGroupCanvasLabel(group, selected)"
-       "            if (!canvasLabelPresent(label)) continue"
+       "            if (canvasLabelMissing(label)) continue"
        "            val center = floatArrayOf("
        "                (group.bbox[0] + group.bbox[2]) / 2f,"
        "                (group.bbox[1] + group.bbox[3]) / 2f"
@@ -8065,7 +8084,7 @@
 	       "    }"
 	       ""
 	       "    private fun drawTouchPointer(canvas: Canvas) {"
-		       "        if (!touchPointerReady(pointerActive, selectMode)) return"
+		       "        if (touchPointerNotReady(pointerActive, selectMode)) return"
 	       "        val anchor = dragEnd ?: dragStart ?: return"
 	       "        val viewAnchor = imageToView(anchor[0], anchor[1])"
 	       "        paint.style = Paint.Style.STROKE"
@@ -8113,7 +8132,7 @@
 	       ""
 	       "    private fun clampOffsets() {"
 	       "        val img = bitmap ?: return"
-	       "        if (!viewDimensionsReady(width, height)) return"
+	       "        if (viewDimensionsMissing(width, height)) return"
 	       "        val displayWidth = rotatedDisplayWidthFloat(rotationDegrees, img.width, img.height)"
 	       "        val displayHeight = rotatedDisplayHeightFloat(rotationDegrees, img.width, img.height)"
 	       "        val scaledWidth = displayWidth * scale"