diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-09-15 23:12:44 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-09-15 23:12:44 +0000 |
| commit | b5a96ad1d0a412c21159d6265e451f7c3aefc796 (patch) | |
| tree | e1284afa3b8ef2bd896342c95d5db55430434813 /app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt | |
| parent | 0cd94d0821661fef3fc34b51011be95034e51a5b (diff) | |
Support pinch-to-zoom for reader text size in the article view
Pinching in ReaderScreen's article WebView now adjusts the same
persisted reader font size the Settings slider controls, instead of
doing nothing (WebView's own page zoom was never enabled, so a pinch
previously had no effect at all).
A ScaleGestureDetector is fed through setOnTouchListener, which always
returns false so it never consumes the touch -- WebView's existing
one-finger scrolling, link taps and long-press-link handling are
unaffected, since ScaleGestureDetector only fires once a second pointer
is down. During the gesture the WebView is scaled live via View.scaleX/
scaleY for immediate visual feedback; at gesture end the scale resets
to 1x and the computed, clamped font size is persisted, which drives
ReaderScreen's existing content-reload-on-fontSizePx-change path to
re-render the actual HTML at the new size.
TDD: pinchZoomFontSizePx (the pure scale-factor -> clamped-px
calculation) was written test-first, red on a TODO() stub, green on the
real implementation -- covered by SettingsTest.
newReaderWebView grew from 6 to 8 parameters to carry the new gesture
hooks, which pushed it over detekt's LongParameterList threshold;
bundled all of its callbacks into a ReaderWebViewCallbacks data class
rather than baselining past it, updating both call sites (ReaderScreen,
LinkReaderScreen). ReaderContent gained no new parameter -- it now
independently remembers its own Settings instance to persist the
pinch-committed size, matching how it already remembers its own db and
scope rather than threading them from ReaderScreen.
LinkReaderScreen's WebView is untouched: it doesn't read the reader
font size setting at all today, so wiring pinch-zoom there would be a
separate, unrelated fix.
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt | 30 |
1 files changed, 22 insertions, 8 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt index a89052c..e2b8aa9 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt @@ -46,6 +46,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -180,6 +181,10 @@ private fun ReaderContent( ) { val context = LocalContext.current val db = remember { AppDatabase.get(context) } + // Independently remembered rather than threaded down from ReaderScreen, matching db/scope + // above -- Settings is a stateless DataStore wrapper, cheap to construct again, and this + // keeps pinch-to-zoom from growing ReaderContent's already-long parameter list any further. + val settings = remember { Settings(context) } val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } @@ -526,19 +531,28 @@ private fun ReaderContent( // changing it in Settings and coming back to an already-open article // picks up the new size, without reloading on every unrelated recomposition. val loadedKey = remember(articleId) { mutableStateOf<Pair<String?, Int>?>(null) } + // newReaderWebView's factory runs once per WebView instance, but fontSizePx can + // change later (e.g. the user tweaks it in Settings while this same article stays + // open) -- rememberUpdatedState keeps the pinch gesture's "what am I scaling from" + // read current instead of frozen at whatever it was on first composition. + val latestFontSizePx by rememberUpdatedState(fontSizePx) AndroidView( modifier = Modifier.fillMaxSize(), factory = { ctx -> newReaderWebView( ctx, - onOpenLink, - onLongPressLink = { longPressedLink = it }, - onYouTubeVideo = { youtubeVideoId = it }, - onScrollChanged = { scrollY -> ReaderScrollPositions.set(articleId, scrollY) }, - // The page's real content height isn't known until layout finishes - // -- scrolling to a saved offset any earlier would just clamp back - // to 0 on a page that hasn't grown tall enough yet. - onPageFinished = { webView -> webView.scrollTo(0, ReaderScrollPositions.get(articleId)) } + ReaderWebViewCallbacks( + onOpenLink = onOpenLink, + onLongPressLink = { longPressedLink = it }, + onYouTubeVideo = { youtubeVideoId = it }, + onScrollChanged = { scrollY -> ReaderScrollPositions.set(articleId, scrollY) }, + // The page's real content height isn't known until layout finishes + // -- scrolling to a saved offset any earlier would just clamp back + // to 0 on a page that hasn't grown tall enough yet. + onPageFinished = { webView -> webView.scrollTo(0, ReaderScrollPositions.get(articleId)) }, + currentFontSizePx = { latestFontSizePx }, + onFontSizeChanged = { newSizePx -> scope.launch { settings.setReaderFontSizePx(newSizePx) } } + ) ).apply { val html = wrapReaderHtml(content, article?.title, article?.publishedAt, article?.imageUrl, fontSizePx) loadDataWithBaseURL(baseUrl, html, "text/html", "UTF-8", null) |
