From f2f5fcacf973d52f14a8988dbd0083ff7f98aa9a Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Thu, 28 Mar 2024 13:44:16 +0800 Subject: [PATCH] Use upstream navigation drawer --- .../{NavigationDrawer2.kt => SideDrawer.kt} | 143 +----------------- .../com/hippo/ehviewer/ui/MainActivity.kt | 33 +++- .../com/hippo/ehviewer/util/MathUtils.kt | 3 + 3 files changed, 35 insertions(+), 144 deletions(-) rename app/src/main/kotlin/androidx/compose/material3/{NavigationDrawer2.kt => SideDrawer.kt} (73%) create mode 100644 app/src/main/kotlin/com/hippo/ehviewer/util/MathUtils.kt diff --git a/app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt b/app/src/main/kotlin/androidx/compose/material3/SideDrawer.kt similarity index 73% rename from app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt rename to app/src/main/kotlin/androidx/compose/material3/SideDrawer.kt index 4dc29885d..784df56a0 100644 --- a/app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt +++ b/app/src/main/kotlin/androidx/compose/material3/SideDrawer.kt @@ -236,7 +236,7 @@ class DrawerState2( * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change. */ @Composable -fun rememberDrawerState( +fun rememberDrawerState2( initialValue: DrawerValue, confirmStateChange: (DrawerValue) -> Boolean = { true }, ): DrawerState2 { @@ -246,147 +246,10 @@ fun rememberDrawerState( } @Composable -fun ModalNavigationDrawer( +fun ModalSideDrawer( drawerContent: @Composable () -> Unit, modifier: Modifier = Modifier, - drawerState: DrawerState2 = rememberDrawerState(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), + drawerState: DrawerState2 = rememberDrawerState2(DrawerValue.Closed), gesturesEnabled: Boolean = true, scrimColor: Color = DrawerDefaults.scrimColor, content: @Composable () -> Unit, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt index 6c727d4a3..1230504bf 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt @@ -49,27 +49,30 @@ import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Whatshot +import androidx.compose.material3.DrawerState import androidx.compose.material3.DrawerState2 import androidx.compose.material3.DrawerValue import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.ModalSideDrawer import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.Scaffold import androidx.compose.material3.ShapeDefaults -import androidx.compose.material3.SideDrawer import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.material3.rememberDrawerState +import androidx.compose.material3.rememberDrawerState2 import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.currentCompositeKeyHash import androidx.compose.runtime.getValue @@ -79,8 +82,13 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshots.SnapshotStateList 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.onGloballyPositioned 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.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.lerp import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.compose.LifecycleResumeEffect 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.updater.AppUpdater import com.hippo.ehviewer.util.addTextToClipboard +import com.hippo.ehviewer.util.calculateFraction import com.hippo.ehviewer.util.displayString import com.hippo.ehviewer.util.getParcelableExtraCompat import com.hippo.ehviewer.util.getUrlFromClipboard @@ -203,7 +213,7 @@ class MainActivity : EhActivity() { val configuration = LocalConfiguration.current val dialogState = LocalDialogState.current val navDrawerState = rememberDrawerState(DrawerValue.Closed) - val sideSheetState = rememberDrawerState(DrawerValue.Closed) + val sideSheetState = rememberDrawerState2(DrawerValue.Closed) val snackbarState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() val navController = rememberNavController() @@ -357,9 +367,11 @@ class MainActivity : EhActivity() { }, ) { LocalTouchSlopProvider(Settings.touchSlopFactor.toFloat()) { + val minOffset = -with(density) { 360.dp.toPx() } ModalNavigationDrawer( drawerContent = { ModalDrawerSheet( + drawerState = navDrawerState, modifier = Modifier.widthIn(max = (configuration.screenWidthDp - 56).dp), windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Start), ) { @@ -397,7 +409,13 @@ class MainActivity : EhActivity() { gesturesEnabled = !drawerLocked || navDrawerState.isOpen, ) { 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 = { if (sheet != null) { 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, gesturesEnabled = sheet != null && !drawerLocked, ) { @@ -507,7 +532,7 @@ class MainActivity : EhActivity() { } } -val LocalNavDrawerState = compositionLocalOf { error("CompositionLocal LocalNavDrawerState not present!") } +val LocalNavDrawerState = compositionLocalOf { error("CompositionLocal LocalNavDrawerState not present!") } val LocalSideSheetState = compositionLocalOf { error("CompositionLocal LocalSideSheetState not present!") } val LocalDrawerLockHandle = compositionLocalOf> { error("CompositionLocal LocalDrawerLockHandle not present!") } val LocalSnackBarHostState = compositionLocalOf { error("CompositionLocal LocalSnackBarHostState not present!") } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/util/MathUtils.kt b/app/src/main/kotlin/com/hippo/ehviewer/util/MathUtils.kt new file mode 100644 index 000000000..1289de9f8 --- /dev/null +++ b/app/src/main/kotlin/com/hippo/ehviewer/util/MathUtils.kt @@ -0,0 +1,3 @@ +package com.hippo.ehviewer.util + +fun calculateFraction(a: Float, b: Float, pos: Float) = ((pos - a) / (b - a)).coerceIn(0f, 1f)