|
|
@@ -2,6 +2,8 @@ package io.legado.app.ui.book.read.sheet
|
|
|
|
|
|
import android.content.Context
|
|
|
import androidx.compose.animation.AnimatedVisibility
|
|
|
+import androidx.compose.animation.core.FastOutSlowInEasing
|
|
|
+import androidx.compose.animation.core.tween
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
import androidx.compose.foundation.layout.Spacer
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
@@ -24,20 +26,28 @@ import androidx.compose.material.icons.filled.Settings
|
|
|
import androidx.compose.material.icons.filled.SkipNext
|
|
|
import androidx.compose.material.icons.filled.SkipPrevious
|
|
|
import androidx.compose.material.icons.filled.Translate
|
|
|
+import androidx.compose.foundation.layout.Box
|
|
|
import androidx.compose.runtime.Composable
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
+import androidx.compose.runtime.derivedStateOf
|
|
|
import androidx.compose.runtime.getValue
|
|
|
import androidx.compose.runtime.mutableIntStateOf
|
|
|
+import androidx.compose.runtime.mutableStateMapOf
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
import androidx.compose.runtime.remember
|
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
|
import androidx.compose.runtime.setValue
|
|
|
import androidx.compose.runtime.snapshotFlow
|
|
|
+import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
|
+import androidx.compose.ui.draw.clipToBounds
|
|
|
import androidx.compose.ui.graphics.vector.ImageVector
|
|
|
+import androidx.compose.ui.layout.onSizeChanged
|
|
|
+import androidx.compose.ui.platform.LocalDensity
|
|
|
import androidx.compose.ui.platform.LocalContext
|
|
|
import androidx.compose.ui.res.stringArrayResource
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
+import androidx.compose.ui.unit.Dp
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
import io.legado.app.R
|
|
|
@@ -89,6 +99,34 @@ internal fun SystemMenuPage(
|
|
|
val pagerState = rememberPagerState(pageCount = { 3 })
|
|
|
var selectedTab by remember { mutableIntStateOf(0) }
|
|
|
|
|
|
+ val pageHeights = remember { mutableStateMapOf<Int, Int>() }
|
|
|
+ val density = LocalDensity.current
|
|
|
+ val animatedHeight by remember(pagerState, density) {
|
|
|
+ derivedStateOf {
|
|
|
+ val pageCount = pagerState.pageCount
|
|
|
+ val position = (pagerState.currentPage + pagerState.currentPageOffsetFraction)
|
|
|
+ .coerceIn(0f, (pageCount - 1).coerceAtLeast(0).toFloat())
|
|
|
+ val floorPage = position.toInt().coerceIn(0, pageCount - 1)
|
|
|
+ val ceilPage = (floorPage + 1).coerceIn(0, pageCount - 1)
|
|
|
+
|
|
|
+ val floorHeight = pageHeights[floorPage] ?: 0
|
|
|
+ val ceilHeight = pageHeights[ceilPage] ?: floorHeight
|
|
|
+
|
|
|
+ val fraction = position - floorPage
|
|
|
+
|
|
|
+ val startHeight = if (floorHeight > 0) floorHeight else (pageHeights.values.firstOrNull() ?: 0)
|
|
|
+ val endHeight = if (ceilHeight > 0) ceilHeight else startHeight
|
|
|
+
|
|
|
+ val interpolated = startHeight + (endHeight - startHeight) * fraction
|
|
|
+
|
|
|
+ if (interpolated > 0) {
|
|
|
+ with(density) { interpolated.toDp() }
|
|
|
+ } else {
|
|
|
+ Dp.Unspecified
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Shared state for sheets
|
|
|
var showColorPicker by remember { mutableStateOf(false) }
|
|
|
var colorPickerId by remember { mutableIntStateOf(0) }
|
|
|
@@ -112,39 +150,58 @@ internal fun SystemMenuPage(
|
|
|
selectedTabIndex = selectedTab,
|
|
|
onTabSelected = { index ->
|
|
|
selectedTab = index
|
|
|
- scope.launch { pagerState.animateScrollToPage(index) }
|
|
|
+ scope.launch {
|
|
|
+ pagerState.animateScrollToPage(
|
|
|
+ page = index,
|
|
|
+ animationSpec = tween(durationMillis = 400, easing = FastOutSlowInEasing)
|
|
|
+ )
|
|
|
+ }
|
|
|
},
|
|
|
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
|
|
|
)
|
|
|
|
|
|
HorizontalPager(
|
|
|
state = pagerState,
|
|
|
- modifier = Modifier.weight(1f, fill = false),
|
|
|
+ verticalAlignment = Alignment.Top,
|
|
|
+ modifier = Modifier
|
|
|
+ .weight(1f, fill = false)
|
|
|
+ .clipToBounds()
|
|
|
+ .pagerHeight(animatedHeight),
|
|
|
) { page ->
|
|
|
- when (page) {
|
|
|
- 0 -> GlobalMenuTab(
|
|
|
- preferences = preferences,
|
|
|
- readBarStyle = preferences.readBarStyle,
|
|
|
- readMenuColorMode = preferences.readMenuColorMode,
|
|
|
- onIntent = onIntent,
|
|
|
- onShowColorPicker = { id, initial ->
|
|
|
- colorPickerId = id
|
|
|
- colorPickerInitial = initial
|
|
|
- showColorPicker = true
|
|
|
- },
|
|
|
- )
|
|
|
- 1 -> BottomBarTab(
|
|
|
- preferences = preferences,
|
|
|
- customIcons = customIcons,
|
|
|
- onIntent = onIntent,
|
|
|
- onShowIconSheet = { showIconSheet = true },
|
|
|
- onShowColorPicker = { id, initial ->
|
|
|
- colorPickerId = id
|
|
|
- colorPickerInitial = initial
|
|
|
- showColorPicker = true
|
|
|
- },
|
|
|
- )
|
|
|
- 2 -> TopBarTab(preferences = preferences, onIntent = onIntent)
|
|
|
+ Box(
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .onSizeChanged { size ->
|
|
|
+ pageHeights[page] = size.height
|
|
|
+ }
|
|
|
+ ) {
|
|
|
+ when (page) {
|
|
|
+ 0 -> GlobalMenuTab(
|
|
|
+ preferences = preferences,
|
|
|
+ readBarStyle = preferences.readBarStyle,
|
|
|
+ readMenuColorMode = preferences.readMenuColorMode,
|
|
|
+ onIntent = onIntent,
|
|
|
+ onShowColorPicker = { id, initial ->
|
|
|
+ colorPickerId = id
|
|
|
+ colorPickerInitial = initial
|
|
|
+ showColorPicker = true
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ 1 -> BottomBarTab(
|
|
|
+ preferences = preferences,
|
|
|
+ customIcons = customIcons,
|
|
|
+ onIntent = onIntent,
|
|
|
+ onShowIconSheet = { showIconSheet = true },
|
|
|
+ onShowColorPicker = { id, initial ->
|
|
|
+ colorPickerId = id
|
|
|
+ colorPickerInitial = initial
|
|
|
+ showColorPicker = true
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ 2 -> TopBarTab(preferences = preferences, onIntent = onIntent)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|