From f2dbb31951ee7b5e2444ca05a8a5b21e733edb9c Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Tue, 20 May 2025 20:01:24 +0800 Subject: [PATCH] Reader app bars improvements (#2337) * Flatten reader app bars layout * Fix reader app bars contract on grey background * Fix unexpected reader app bars visibility changes --- .../hippo/ehviewer/ui/reader/ReaderScreen.kt | 4 +- .../ui/reader/SliderPagerDoubleSync.kt | 3 +- .../tachiyomi/ui/reader/BottomReaderBar.kt | 5 +- .../tachiyomi/ui/reader/ChapterNavigator.kt | 83 ++++++++----------- .../tachiyomi/ui/reader/ReaderAppBars.kt | 36 ++++---- 5 files changed, 53 insertions(+), 78 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt index 034832771..052892433 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt @@ -232,9 +232,6 @@ fun AnimatedVisibilityScope.ReaderScreen(pageLoader: PageLoader, info: BaseGalle } val bgColor by collectBackgroundColorAsState() syncState.Sync(isWebtoon) { appbarVisible = false } - LaunchedEffect(isWebtoon) { - appbarVisible = false - } if (fullscreen) { LaunchedEffect(Unit) { snapshotFlow { appbarVisible }.collect { @@ -372,6 +369,7 @@ fun AnimatedVisibilityScope.ReaderScreen(pageLoader: PageLoader, info: BaseGalle } } }, + modifier = Modifier.align(Alignment.BottomCenter), ) } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/SliderPagerDoubleSync.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/SliderPagerDoubleSync.kt index 1b4b52a74..bffcfe743 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/SliderPagerDoubleSync.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/SliderPagerDoubleSync.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import com.hippo.ehviewer.gallery.PageLoader import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.filterNotNull @Stable @@ -59,7 +60,7 @@ class SliderPagerDoubleSync( } if (sliderFollowPager) { LaunchedEffect(currentIndexFlow) { - currentIndexFlow.collect { index -> + currentIndexFlow.drop(1).collect { index -> sliderValue = index + 1 pageLoader.startPage = index onPageSelected() diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/BottomReaderBar.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/BottomReaderBar.kt index 66a540a20..4621f35f2 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/BottomReaderBar.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/BottomReaderBar.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.outlined.Settings -import androidx.compose.material3.BottomAppBarDefaults import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox @@ -16,6 +15,7 @@ import androidx.compose.material3.FlexibleBottomAppBar import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -77,8 +77,9 @@ fun BottomReaderBar(onClickSettings: () -> Unit) = FlexibleBottomAppBar( ) } +// Use surface container highest to have a higher contract on grey background val toolbarColor - @Composable get() = BottomAppBarDefaults.containerColor.copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f) + @Composable get() = MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f) @Composable private fun DropdownIconButton( diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt index 1e6be5f1b..c4da0f551 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt @@ -4,9 +4,8 @@ import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsDraggedAsState import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Slider import androidx.compose.material3.SliderDefaults import androidx.compose.material3.Text @@ -35,61 +34,45 @@ fun ChapterNavigator( currentPage: Int, totalPages: Int, onSliderValueChange: (Int) -> Unit, -) { +) = CompositionLocalProvider(LocalLayoutDirection provides if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr) { val windowSizeClass = LocalWindowSizeClass.current val horizontalPadding = if (windowSizeClass.isExpanded) 24.dp else 16.dp - val layoutDirection = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr val configuration = LocalConfiguration.current val maxTickCount = configuration.screenWidthDp / (SliderDefaults.TickSize.value * 2.5f).roundToInt() val showTicks = totalPages < maxTickCount - - // We explicitly handle direction based on the reader viewer rather than the system direction - CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { - Row( - modifier = Modifier.fillMaxWidth().padding(horizontal = horizontalPadding), - verticalAlignment = Alignment.CenterVertically, - ) { - if (totalPages > 1) { - CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) { - Row( - modifier = Modifier.weight(1f).clip(RoundedCornerShape(24.dp)).background(toolbarColor).padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text(text = currentPage.toString()) - - val interactionSource = remember { MutableInteractionSource() } - if (showTicks) { - val sliderDragged by interactionSource.collectIsDraggedAsState() - val hapticFeedback = rememberHapticFeedback() - LaunchedEffect(currentPage) { - if (sliderDragged) { - hapticFeedback.performHapticFeedback(HapticFeedbackType.MOVE) - } - } - } - Slider( - modifier = Modifier.weight(1f).padding(horizontal = 8.dp), - value = currentPage.toFloat(), - valueRange = 1f..totalPages.toFloat(), - steps = totalPages - 2, - onValueChange = { - onSliderValueChange(it.roundToInt() - 1) - }, - interactionSource = interactionSource, - colors = if (showTicks) { - SliderDefaults.colors() - } else { - SliderDefaults.colors( - activeTickColor = Color.Transparent, - inactiveTickColor = Color.Transparent, - ) - }, - ) - - Text(text = totalPages.toString()) - } + Row( + modifier = Modifier.padding(horizontal = horizontalPadding).clip(CircleShape).background(toolbarColor).padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(text = "$currentPage") + val interactionSource = remember { MutableInteractionSource() } + if (showTicks) { + val sliderDragged by interactionSource.collectIsDraggedAsState() + val hapticFeedback = rememberHapticFeedback() + LaunchedEffect(currentPage) { + if (sliderDragged) { + hapticFeedback.performHapticFeedback(HapticFeedbackType.MOVE) } } } + Slider( + modifier = Modifier.weight(1f).padding(horizontal = 8.dp), + value = currentPage.toFloat(), + valueRange = 1f..totalPages.toFloat(), + steps = totalPages - 2, + onValueChange = { + onSliderValueChange(it.roundToInt() - 1) + }, + interactionSource = interactionSource, + colors = if (showTicks) { + SliderDefaults.colors() + } else { + SliderDefaults.colors( + activeTickColor = Color.Transparent, + inactiveTickColor = Color.Transparent, + ) + }, + ) + Text(text = "$totalPages") } } diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderAppBars.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderAppBars.kt index 070511e67..e7b66c036 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderAppBars.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderAppBars.kt @@ -6,10 +6,8 @@ import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsPadding @@ -30,27 +28,21 @@ fun ReaderAppBars( onSliderValueChange: (Int) -> Unit, onClickSettings: () -> Unit, modifier: Modifier = Modifier, -) = Column( - modifier = modifier.fillMaxHeight().windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)), - verticalArrangement = Arrangement.SpaceBetween, +) = AnimatedVisibility( + visible = visible, + modifier = modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)), + enter = slideInVertically(initialOffsetY = { it }, animationSpec = animationSpec), + exit = slideOutVertically(targetOffsetY = { it }, animationSpec = animationSpec), ) { - Spacer(modifier = Modifier.weight(1f)) - - AnimatedVisibility( - visible = visible, - enter = slideInVertically(initialOffsetY = { it }, animationSpec = animationSpec), - exit = slideOutVertically(targetOffsetY = { it }, animationSpec = animationSpec), - ) { - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - if (showSeekBar) { - ChapterNavigator( - isRtl = isRtl, - currentPage = currentPage, - totalPages = totalPages, - onSliderValueChange = onSliderValueChange, - ) - } - BottomReaderBar(onClickSettings = onClickSettings) + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + if (showSeekBar && totalPages > 1) { + ChapterNavigator( + isRtl = isRtl, + currentPage = currentPage, + totalPages = totalPages, + onSliderValueChange = onSliderValueChange, + ) } + BottomReaderBar(onClickSettings = onClickSettings) } }