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
This commit is contained in:
FooIbar authored and GitHub committed 2025-05-20 20:01:24 +08:00
1 parent fe371f1d66
commit f2dbb31951
5 files changed
+53 -78

No files matched your search

@@ -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),
)
}
}
@@ -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()
@@ -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(
@@ -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")
}
}
@@ -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)
}
}