Use upstream navigation drawer

This commit is contained in:
FooIbar authored and renovate[bot] committed 2024-03-28 15:48:16 +08:00
1 parent 52ded265f3
commit f2f5fcacf9
3 files changed
+35 -144

No files matched your search

@@ -236,7 +236,7 @@ class DrawerState2(
* @param confirmStateChange Optional callback invoked to confirm or veto a pending state change. * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
*/ */
@Composable @Composable
fun rememberDrawerState( fun rememberDrawerState2(
initialValue: DrawerValue, initialValue: DrawerValue,
confirmStateChange: (DrawerValue) -> Boolean = { true }, confirmStateChange: (DrawerValue) -> Boolean = { true },
): DrawerState2 { ): DrawerState2 {
@@ -246,147 +246,10 @@ fun rememberDrawerState(
} }
@Composable @Composable
fun ModalNavigationDrawer( fun ModalSideDrawer(
drawerContent: @Composable () -> Unit, drawerContent: @Composable () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
drawerState: DrawerState2 = rememberDrawerState(DrawerValue.Closed), drawerState: DrawerState2 = rememberDrawerState2(DrawerValue.Closed),
gesturesEnabled: Boolean = true,
scrimColor: Color = DrawerDefaults.scrimColor,
content: @Composable () -> Unit,
) {
val scope = rememberCoroutineScope()
val density = LocalDensity.current
var minValue by remember { mutableFloatStateOf(-with(density) { ContainerWidth.toPx() }) }
val maxValue = 0f
SideEffect {
drawerState.density = density
drawerState.anchoredDraggableState.updateAnchors(
DraggableAnchors {
DrawerValue.Closed at minValue
DrawerValue.Open at maxValue
},
)
}
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
val velocityTracker = remember { VelocityTracker() }
val dragModifier = if (gesturesEnabled) {
Modifier.pointerInput(isRtl) {
val multiplier = if (isRtl) -1 else 1
awaitEachGesture {
fun canConsume(slop: Float) = (slop > 0 && drawerState.isClosed) || (slop < 0 && drawerState.isOpen)
val down = awaitFirstDown(requireUnconsumed = false)
var ignore = false
val drag = awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
val overSlop = over * multiplier
val canConsume = canConsume(overSlop)
if (canConsume && !ignore) {
velocityTracker.addPointerInputChange(change)
change.consume()
drawerState.anchoredDraggableState.dispatchRawDelta(overSlop)
} else {
ignore = true
}
}
if (drag != null) {
horizontalDrag(drag.id) {
velocityTracker.addPointerInputChange(it)
drawerState.anchoredDraggableState.dispatchRawDelta(it.positionChange().x * multiplier)
}
val velocity = velocityTracker.calculateVelocity()
velocityTracker.resetTracking()
scope.launch {
drawerState.anchoredDraggableState.settle(velocity.x * multiplier)
}
}
}
}
} else {
Modifier
}
Box(modifier.fillMaxSize().then(dragModifier)) {
val radius by remember {
snapshotFlow {
val step = calculateFraction(minValue, maxValue, drawerState.currentOffset)
lerp(0, 10, step).dp
}
}.collectAsState(0.dp)
val blurModifier = Modifier.graphicsLayer {
if (radius != 0.dp) {
renderEffect = BlurEffect(radius.toPx(), radius.toPx(), TileMode.Clamp)
shape = RectangleShape
clip = true
}
}
Box(modifier = blurModifier) {
content()
}
Scrim(
open = drawerState.isOpen,
onClose = {
if (gesturesEnabled && drawerState.confirmStateChange(DrawerValue.Closed)) {
scope.launch { drawerState.close() }
}
},
fraction = {
calculateFraction(minValue, maxValue, drawerState.requireOffset())
},
color = scrimColor,
)
val predictiveState by animateFloatMergeOneWayPredictiveBackAsState(drawerState.isOpen) {
drawerState.close()
}
val absoluteElevation = LocalAbsoluteTonalElevation.current + DrawerDefaults.ModalDrawerElevation
val predictiveModifier = if (drawerState.isOpen) {
if (predictiveState > 0) {
Modifier.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val multiplierX = lerp(1f, 1.05f, predictiveState)
val multiplierY = lerp(1f, 0.95f, predictiveState)
val scaledWidth = (multiplierX * placeable.width).roundToInt()
val scaledHeight = (multiplierY * placeable.height).roundToInt()
val reMeasured = measurable.measure(Constraints.fixed(scaledWidth, scaledHeight))
layout(scaledWidth, scaledHeight) {
reMeasured.placeRelative(scaledWidth - placeable.width, 0)
}
}.background(
color = MaterialTheme.colorScheme.surfaceColorAtElevation(absoluteElevation),
shape = DrawerDefaults.shape,
)
} else {
Modifier.graphicsLayer {
val scale = lerp(1f, 0.95f, -predictiveState)
scaleX = scale
scaleY = scale
}.offset {
IntOffset(lerp(0f, minValue * 0.05f, -predictiveState).roundToInt(), 0)
}
}
} else {
Modifier.onGloballyPositioned {
val w = it.size.width
if (w != 0) {
minValue = -it.size.width.toFloat()
}
}
}
Box(
modifier = Modifier.offset {
IntOffset(drawerState.requireOffset().roundToInt(), 0)
} then predictiveModifier.align(Alignment.CenterStart),
contentAlignment = Alignment.CenterEnd,
) {
drawerContent()
}
}
}
@Composable
fun SideDrawer(
drawerContent: @Composable () -> Unit,
modifier: Modifier = Modifier,
drawerState: DrawerState2 = rememberDrawerState(DrawerValue.Closed),
gesturesEnabled: Boolean = true, gesturesEnabled: Boolean = true,
scrimColor: Color = DrawerDefaults.scrimColor, scrimColor: Color = DrawerDefaults.scrimColor,
content: @Composable () -> Unit, content: @Composable () -> Unit,
@@ -49,27 +49,30 @@ import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Whatshot import androidx.compose.material.icons.filled.Whatshot
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerState2 import androidx.compose.material3.DrawerState2
import androidx.compose.material3.DrawerValue import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.ModalSideDrawer
import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ShapeDefaults import androidx.compose.material3.ShapeDefaults
import androidx.compose.material3.SideDrawer
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberDrawerState import androidx.compose.material3.rememberDrawerState
import androidx.compose.material3.rememberDrawerState2
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.currentCompositeKeyHash import androidx.compose.runtime.currentCompositeKeyHash
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -79,8 +82,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlurEffect
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
@@ -90,6 +98,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.LifecycleResumeEffect import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
@@ -126,6 +135,7 @@ import com.hippo.ehviewer.ui.tools.LocalTouchSlopProvider
import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass
import com.hippo.ehviewer.updater.AppUpdater import com.hippo.ehviewer.updater.AppUpdater
import com.hippo.ehviewer.util.addTextToClipboard import com.hippo.ehviewer.util.addTextToClipboard
import com.hippo.ehviewer.util.calculateFraction
import com.hippo.ehviewer.util.displayString import com.hippo.ehviewer.util.displayString
import com.hippo.ehviewer.util.getParcelableExtraCompat import com.hippo.ehviewer.util.getParcelableExtraCompat
import com.hippo.ehviewer.util.getUrlFromClipboard import com.hippo.ehviewer.util.getUrlFromClipboard
@@ -203,7 +213,7 @@ class MainActivity : EhActivity() {
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val dialogState = LocalDialogState.current val dialogState = LocalDialogState.current
val navDrawerState = rememberDrawerState(DrawerValue.Closed) val navDrawerState = rememberDrawerState(DrawerValue.Closed)
val sideSheetState = rememberDrawerState(DrawerValue.Closed) val sideSheetState = rememberDrawerState2(DrawerValue.Closed)
val snackbarState = remember { SnackbarHostState() } val snackbarState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val navController = rememberNavController() val navController = rememberNavController()
@@ -357,9 +367,11 @@ class MainActivity : EhActivity() {
}, },
) { ) {
LocalTouchSlopProvider(Settings.touchSlopFactor.toFloat()) { LocalTouchSlopProvider(Settings.touchSlopFactor.toFloat()) {
val minOffset = -with(density) { 360.dp.toPx() }
ModalNavigationDrawer( ModalNavigationDrawer(
drawerContent = { drawerContent = {
ModalDrawerSheet( ModalDrawerSheet(
drawerState = navDrawerState,
modifier = Modifier.widthIn(max = (configuration.screenWidthDp - 56).dp), modifier = Modifier.widthIn(max = (configuration.screenWidthDp - 56).dp),
windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Start), windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Start),
) { ) {
@@ -397,7 +409,13 @@ class MainActivity : EhActivity() {
gesturesEnabled = !drawerLocked || navDrawerState.isOpen, gesturesEnabled = !drawerLocked || navDrawerState.isOpen,
) { ) {
val sheet = sideSheet.firstOrNull() val sheet = sideSheet.firstOrNull()
SideDrawer( val radius by remember {
snapshotFlow {
val step = calculateFraction(minOffset, 0f, navDrawerState.currentOffset)
with(density) { lerp(0, 10, step).dp.toPx() }
}
}.collectAsState(0f)
ModalSideDrawer(
drawerContent = { drawerContent = {
if (sheet != null) { if (sheet != null) {
ModalDrawerSheet( ModalDrawerSheet(
@@ -411,6 +429,13 @@ class MainActivity : EhActivity() {
} }
} }
}, },
modifier = Modifier.graphicsLayer {
if (radius != 0f) {
renderEffect = BlurEffect(radius, radius, TileMode.Clamp)
shape = RectangleShape
clip = true
}
},
drawerState = sideSheetState, drawerState = sideSheetState,
gesturesEnabled = sheet != null && !drawerLocked, gesturesEnabled = sheet != null && !drawerLocked,
) { ) {
@@ -507,7 +532,7 @@ class MainActivity : EhActivity() {
} }
} }
val LocalNavDrawerState = compositionLocalOf<DrawerState2> { error("CompositionLocal LocalNavDrawerState not present!") } val LocalNavDrawerState = compositionLocalOf<DrawerState> { error("CompositionLocal LocalNavDrawerState not present!") }
val LocalSideSheetState = compositionLocalOf<DrawerState2> { error("CompositionLocal LocalSideSheetState not present!") } val LocalSideSheetState = compositionLocalOf<DrawerState2> { error("CompositionLocal LocalSideSheetState not present!") }
val LocalDrawerLockHandle = compositionLocalOf<SnapshotStateList<Int>> { error("CompositionLocal LocalDrawerLockHandle not present!") } val LocalDrawerLockHandle = compositionLocalOf<SnapshotStateList<Int>> { error("CompositionLocal LocalDrawerLockHandle not present!") }
val LocalSnackBarHostState = compositionLocalOf<SnackbarHostState> { error("CompositionLocal LocalSnackBarHostState not present!") } val LocalSnackBarHostState = compositionLocalOf<SnackbarHostState> { error("CompositionLocal LocalSnackBarHostState not present!") }
@@ -0,0 +1,3 @@
package com.hippo.ehviewer.util
fun calculateFraction(a: Float, b: Float, pos: Float) = ((pos - a) / (b - a)).coerceIn(0f, 1f)