Generate SSD scale listener from typed Jerboa

ober

f30df69acc69b1998134d7f65f8855e970ccf0be

diff --git a/.build.yml b/.build.yml
index 555da11..64b8039 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#3192be39569ab234ee7ffd3e2a7cf766ac1ae7ab"
+  - "https://git.sr.ht/~lisp/jerboa#f833c6c8887a23fedbd207d5d4e48df3ea2afc06"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = 3192be39569ab234ee7ffd3e2a7cf766ac1ae7ab
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = df37b61fef97a1800e17cf23edf34310f9f1107c
+      test "$(git -C ../jerboa rev-parse HEAD)" = f833c6c8887a23fedbd207d5d4e48df3ea2afc06
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 3128143e7858a334acf4ef32d1db19f675f618cd
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index f447c20..f3e0442 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "3192be39569ab234ee7ffd3e2a7cf766ac1ae7ab",
-    "tree": "df37b61fef97a1800e17cf23edf34310f9f1107c"
+    "commit": "f833c6c8887a23fedbd207d5d4e48df3ea2afc06",
+    "tree": "3128143e7858a334acf4ef32d1db19f675f618cd"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 404071b..df55347 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=3192be39569ab234ee7ffd3e2a7cf766ac1ae7ab # gitsafe:ignore
-jerboa_tree=df37b61fef97a1800e17cf23edf34310f9f1107c # gitsafe:ignore
+jerboa_commit=f833c6c8887a23fedbd207d5d4e48df3ea2afc06 # gitsafe:ignore
+jerboa_tree=3128143e7858a334acf4ef32d1db19f675f618cd # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 7565563..e9d3de7 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -4611,6 +4611,7 @@
                       (java io InputStream)
                       (java io OutputStream)
                       (android app AlertDialog)
+                      (android content Context)
                       (android graphics Bitmap)
                       (android content Intent)
                       (android database Cursor)
@@ -4618,6 +4619,8 @@
                       (android os ParcelFileDescriptor)
                       (android text Editable)
                       (android text TextWatcher)
+                      (android view ScaleGestureDetector)
+                      (android view ScaleGestureDetector SimpleOnScaleGestureListener)
                       (android view View)
                       (android view Window)
                       (android widget AutoCompleteTextView)
@@ -4702,6 +4705,7 @@
                 reviewViewRestoreViewport
                 reviewViewRestoreViewportAfterLayout
                 reviewViewClearInteractionState
+                reviewViewMakeScaleGestureDetector
                 mainActivityInstallReviewViewCallbacks
                 mainActivitySelectGroupId
                 mainActivitySelectRequestedGroup
@@ -4800,6 +4804,7 @@
         (type AutoCompleteTextView)
         (type Button)
         (type CharSequence)
+        (type Context)
         (type Editable)
         (type EditText)
         (type File)
@@ -4813,6 +4818,8 @@
         (type Window)
         (type DocumentFile)
         (type JSONObject)
+        (type ScaleGestureDetector)
+        (type SimpleOnScaleGestureListener)
         (type SsdSession)
         (type SsdReviewView)
         (type MainActivity)
@@ -4978,6 +4985,19 @@
                   (store : TruthStore)
                   (treeUri : Uri)) : (Pair Int32 Int32)
           (kotlin-member-call syncTree))
+        (extern (makeScaleGestureDetector
+                  (context : Context)
+                  (listener : SimpleOnScaleGestureListener)) : ScaleGestureDetector
+          (kotlin-call ScaleGestureDetector))
+        (extern (scaleGestureDetectorFocusX
+                  (detector : ScaleGestureDetector)) : Float32
+          (kotlin-member-get focusX))
+        (extern (scaleGestureDetectorFocusY
+                  (detector : ScaleGestureDetector)) : Float32
+          (kotlin-member-get focusY))
+        (extern (scaleGestureDetectorScaleFactor
+                  (detector : ScaleGestureDetector)) : Float32
+          (kotlin-member-get scaleFactor))
         (extern (reviewViewCaptureViewport (view : SsdReviewView)) : (Nullable ViewportState)
           (kotlin-member-call captureViewport))
         (extern (reviewViewRestoreViewportRaw
@@ -5023,6 +5043,12 @@
                   (view : SsdReviewView)
                   (group : SsdGroup)) : Unit
           (kotlin-member-call centerOnGroup))
+        (extern (reviewViewZoomAtRaw
+                  (view : SsdReviewView)
+                  (viewX : Float32)
+                  (viewY : Float32)
+                  (factor : Float32)) : Unit
+          (kotlin-member-call zoomAt))
         (extern (reviewViewSelectedGroupIdSet (view : SsdReviewView)
                                               (id : (Nullable String))) : Unit
           (kotlin-member-set selectedGroupId))
@@ -5774,6 +5800,36 @@
           (reviewViewCenterOnGroupRaw view group))
         (def (reviewViewSelectMode (view : SsdReviewView)) : Bool
           (reviewViewSelectModeRaw view))
+        (def (reviewViewZoomAt
+               (view : SsdReviewView)
+               (viewX : Float32)
+               (viewY : Float32)
+               (factor : Float32)) : Unit
+          (reviewViewZoomAtRaw view viewX viewY factor))
+        (def (reviewViewHandleScaleGesture
+               (view : SsdReviewView)
+               (detector : ScaleGestureDetector)) : Bool
+          (if (touchSelectionActive (reviewViewSelectMode view))
+            #f
+            (begin
+              (reviewViewZoomAt
+                view
+                (scaleGestureDetectorFocusX detector)
+                (scaleGestureDetectorFocusY detector)
+                (scaleGestureDetectorScaleFactor detector))
+              #t)))
+        (def (reviewViewMakeScaleGestureListener
+               (view : SsdReviewView)) : SimpleOnScaleGestureListener
+          (object (new SimpleOnScaleGestureListener)
+            (override (onScale
+                        (detector : ScaleGestureDetector)) : Bool
+              (reviewViewHandleScaleGesture view detector))))
+        (def (reviewViewMakeScaleGestureDetector
+               (context : Context)
+               (view : SsdReviewView)) : ScaleGestureDetector
+          (makeScaleGestureDetector
+            context
+            (reviewViewMakeScaleGestureListener view)))
         (def (reviewViewCaptureViewportForSave
                (view : SsdReviewView)) : (Nullable ViewportState)
           (reviewViewCaptureViewport view))
@@ -9551,13 +9607,7 @@
 	       "    internal var pointerActive = false"
 	       "    internal var moved = false"
        ""
-	       "    private val scaler = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {"
-	       "        override fun onScale(detector: ScaleGestureDetector): Boolean {"
-       "            if (touchSelectionActive(selectMode)) return false"
-	       "            zoomAt(detector.focusX, detector.focusY, detector.scaleFactor)"
-	       "            return true"
-	       "        }"
-	       "    })"
+	       "    private val scaler = reviewViewMakeScaleGestureDetector(context, this)"
        ""
        "    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {"
        "        if (viewSizeChangeNeedsFitForBitmap(oldw, oldh, bitmap)) {"
@@ -9853,7 +9903,7 @@
 	       "        matrix.invert(inverse)"
 	       "    }"
 	       ""
-	       "    private fun zoomAt(viewX: Float, viewY: Float, factor: Float) {"
+	       "    internal fun zoomAt(viewX: Float, viewY: Float, factor: Float) {"
 	       "        val before = viewToImage(viewX, viewY)"
 	       "        reviewViewSetScale(this, clampScale(scale * factor))"
 	       "        updateMatrix()"