From 53a1bd99593fd68f0f6fb35b489255e4b6a3800d Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:08:12 +0800 Subject: [PATCH] [New Reader] Implement landscape zoom --- .../kotlin/com/hippo/ehviewer/Settings.kt | 8 ++++ .../hippo/ehviewer/ui/reader/GalleryPager.kt | 44 +++++++++++++++---- .../com/hippo/ehviewer/ui/reader/Utils.kt | 11 ++++- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt b/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt index 946d1428c..9dfb22186 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt @@ -70,6 +70,14 @@ inline fun PrefDelegate.collectAsState(crossinline transform: @Disallo return flow.collectAsState(transform(init)) } +@Stable +@Composable +inline fun PrefDelegate.collectAsState(key1: Any?, crossinline transform: @DisallowComposableCalls (T) -> R): State { + val flow = remember(key1) { valueFlow().map { transform(it) } } + val init = getValue() + return flow.collectAsState(transform(init)) +} + @Stable @Composable fun PrefDelegate.collectAsState(): State { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt index b5dcb3487..08d1c985a 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/GalleryPager.kt @@ -31,6 +31,8 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.FixedScale import androidx.compose.ui.layout.times import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize import com.hippo.ehviewer.Settings @@ -50,6 +52,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType.WEBTOON import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.saket.telephoto.zoomable.DoubleClickToZoomListener import me.saket.telephoto.zoomable.ZoomSpec @@ -75,10 +78,11 @@ fun GalleryPager( DEFAULT, LEFT_TO_RIGHT, RIGHT_TO_LEFT, VERTICAL -> { val isVertical = type == VERTICAL val scaleType by Settings.imageScaleType.collectAsState() - val alignment by Settings.zoomStart.collectAsState { - Alignment.fromPreferences(it, isVertical) + val landscapeZoom by Settings.landscapeZoom.collectAsState() + val alignment by Settings.zoomStart.collectAsState(type) { + Alignment.fromPreferences(it, type) } - val navigatorState = Settings.readerPagerNav.collectAsState { + val navigatorState = Settings.readerPagerNav.collectAsState(isVertical) { ViewerNavigation.fromPreference(it, isVertical) } val invertMode by Settings.readerPagerNavInverted.collectAsState { @@ -105,6 +109,7 @@ fun GalleryPager( pageLoader = pageLoader, isRtl = false, scaleType = scaleType, + landscapeZoom = landscapeZoom, alignment = alignment, layoutSize = layoutSize, navigatorState = navigatorState, @@ -116,11 +121,12 @@ fun GalleryPager( } } else { val isRtl = type == RIGHT_TO_LEFT + val isRtlLayout = LocalLayoutDirection.current == LayoutDirection.Rtl HorizontalPager( state = pagerState, modifier = modifier, contentPadding = contentPadding, - reverseLayout = isRtl, + reverseLayout = isRtl xor isRtlLayout, key = { it }, ) { index -> val page = items[index] @@ -129,6 +135,7 @@ fun GalleryPager( pageLoader = pageLoader, isRtl = isRtl, scaleType = scaleType, + landscapeZoom = landscapeZoom, alignment = alignment, layoutSize = layoutSize, navigatorState = navigatorState, @@ -212,6 +219,7 @@ private fun PageContainer( pageLoader: PageLoader2, isRtl: Boolean, scaleType: Int, + landscapeZoom: Boolean, alignment: Alignment.Horizontal, layoutSize: Size, navigatorState: State, @@ -222,7 +230,7 @@ private fun PageContainer( ) { @Suppress("NAME_SHADOWING") val isRtl by rememberUpdatedState(isRtl) - val zoomableState = rememberZoomableState(zoomSpec = zoomSpec) + val zoomableState = rememberZoomableState(zoomSpec = pagerZoomSpec) val status by page.status.collectAsState() if (status == Page.State.READY) { val size = page.image!!.rect.size.toSize() @@ -250,6 +258,18 @@ private fun PageContainer( val contentLocation = ZoomableContentLocation.scaledInsideAndCenterAligned(size) zoomableState.setContentLocation(contentLocation) } + if (landscapeZoom && contentScale == ContentScale.Fit && size.width > size.height) { + LaunchedEffect(alignment) { + delay(500) + if (zoomableState.zoomFraction == 0f) { + val contentSize = zoomableState.transformedContentBounds.size + val scale = ContentScale.FillHeight.computeScaleFactor(contentSize, layoutSize) + val targetScale = scale.scaleX.coerceAtMost(zoomableState.zoomSpec.maxZoomFactor) + val offset = alignment.align(0, layoutSize.width.toInt(), LayoutDirection.Ltr) + zoomableState.zoomTo(targetScale, Offset(offset.toFloat(), 0f)) + } + } + } } Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { PagerItem( @@ -350,13 +370,21 @@ suspend fun LazyListState.performScrollBy(value: Float) { } object DoubleTapZoom : DoubleClickToZoomListener { - private val listener = DoubleClickToZoomListener.cycle(maxZoomFactor = 2f) - override suspend fun onDoubleClick(state: ZoomableState, centroid: Offset) { if (Settings.doubleTapToZoom.value) { - listener.onDoubleClick(state, centroid) + val zoomFraction = state.zoomFraction ?: return // Content isn't ready yet + if (zoomFraction > 0.05f) { + state.resetZoom() + } else { + // Workaround for https://github.com/saket/telephoto/issues/45 + state.zoomTo( + zoomFactor = state.contentTransformation.scaleMetadata.initialScale.scaleX * 2f, + centroid = centroid, + ) + } } } } +private val pagerZoomSpec = ZoomSpec(maxZoomFactor = 5f) private val zoomSpec = ZoomSpec(maxZoomFactor = 3f) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/Utils.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/Utils.kt index 6ba8e3bb9..217b3f37b 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/Utils.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/Utils.kt @@ -13,6 +13,9 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.FixedScale import com.hippo.ehviewer.Settings import eu.kanade.tachiyomi.ui.reader.setting.OrientationType +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType.RIGHT_TO_LEFT +import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType.VERTICAL import kotlinx.coroutines.flow.onCompletion fun Window.updateKeepScreenOn(enabled: Boolean) { @@ -58,8 +61,12 @@ fun Activity.setCustomBrightnessValue(value: Int) { window.attributes = window.attributes.apply { screenBrightness = readerBrightness } } -fun Alignment.Companion.fromPreferences(value: Int, isVertical: Boolean) = when (value) { - 1 -> if (isVertical) CenterHorizontally else Start +fun Alignment.Companion.fromPreferences(value: Int, mode: ReadingModeType) = when (value) { + 1 -> when (mode) { + VERTICAL -> CenterHorizontally + RIGHT_TO_LEFT -> AbsoluteAlignment.Right + else -> AbsoluteAlignment.Left + } 2 -> AbsoluteAlignment.Left 3 -> AbsoluteAlignment.Right else -> CenterHorizontally