Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c672395 authored by Ale Nijamkin's avatar Ale Nijamkin
Browse files

[Media] Fixes swipe to reveal in RTL

1. Makes OffsetOverscrollEffect customizable such that callers can opt out
of having it use placeRelative
2. Makes use of the new parameter in SwipeToReveal

Bug: 403558944
Test: manually verified in the Compose Gallery app that the
non-dismissible media card carousel, in RTL, animates its overscroll in
the right direction - both when pulling from the card or from the gear
icon. Also made sure that the LTR case isn't harmed
Flag: EXEMPT code not yet used in production

Change-Id: Ib5e2f94c1e984cd0700385dca46d755d67ce9e5f
parent f77b0177
Loading
Loading
Loading
Loading
+34 −9
Original line number Diff line number Diff line
@@ -36,14 +36,22 @@ import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope

/**
 * Returns a [remember]ed [OffsetOverscrollEffect].
 *
 * @param placeRelatively If `true`, the overscrolled content will be placed relatively, meaning
 *   that its placement will be flipped in right-to-left layouts. If `false`, the content will be
 *   placed absolutely - same placement for RTL and LTR.
 */
@Composable
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun rememberOffsetOverscrollEffect(
    animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.slowSpatialSpec()
    animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.slowSpatialSpec(),
    placeRelatively: Boolean = true,
): OffsetOverscrollEffect {
    val animationScope = rememberCoroutineScope()
    return remember(animationScope, animationSpec) {
        OffsetOverscrollEffect(animationScope, animationSpec)
        OffsetOverscrollEffect(animationScope, animationSpec, placeRelatively)
    }
}

@@ -63,13 +71,20 @@ data class OffsetOverscrollEffectFactory(
    private val animationSpec: AnimationSpec<Float>,
) : OverscrollFactory {
    override fun createOverscrollEffect(): OverscrollEffect {
        return OffsetOverscrollEffect(animationScope, animationSpec)
        return OffsetOverscrollEffect(
            animationScope = animationScope,
            animationSpec = animationSpec,
            placeRelatively = true,
        )
    }
}

/** An [OverscrollEffect] that offsets the content by the overscroll value. */
class OffsetOverscrollEffect(animationScope: CoroutineScope, animationSpec: AnimationSpec<Float>) :
    BaseContentOverscrollEffect(animationScope, animationSpec) {
class OffsetOverscrollEffect(
    animationScope: CoroutineScope,
    animationSpec: AnimationSpec<Float>,
    placeRelatively: Boolean,
) : BaseContentOverscrollEffect(animationScope, animationSpec) {
    override val node: DelegatableNode =
        object : Modifier.Node(), LayoutModifierNode {
            override fun MeasureScope.measure(
@@ -80,11 +95,21 @@ class OffsetOverscrollEffect(animationScope: CoroutineScope, animationSpec: Anim
                return layout(placeable.width, placeable.height) {
                    val offsetPx = computeOffset(density = this@measure, overscrollDistance)
                    if (offsetPx != 0) {
                        if (placeRelatively) {
                            placeable.placeRelativeWithLayer(
                                with(requireConverter()) { offsetPx.toIntOffset() }
                            )
                        } else {
                            placeable.placeWithLayer(
                                with(requireConverter()) { offsetPx.toIntOffset() }
                            )
                        }
                    } else {
                        if (placeRelatively) {
                            placeable.placeRelative(0, 0)
                        } else {
                            placeable.place(0, 0)
                        }
                    }
                }
            }
+3 −2
Original line number Diff line number Diff line
@@ -244,7 +244,8 @@ private fun CardCarouselContent(
                    userScrollEnabled = isSwipingEnabled,
                    pageSpacing = 8.dp,
                    key = { index: Int -> viewModel.cards[index].key },
                    overscrollEffect = overscrollEffect ?: rememberOffsetOverscrollEffect(),
                    overscrollEffect =
                        overscrollEffect ?: rememberOffsetOverscrollEffect(placeRelatively = false),
                ) { pageIndex: Int ->
                    Card(
                        viewModel = viewModel.cards[pageIndex],
@@ -269,7 +270,7 @@ private fun CardCarouselContent(
        if (behavior.isCarouselDismissible) {
            SwipeToDismiss(content = { PagerContent() }, onDismissed = onDismissed)
        } else {
            val overscrollEffect = rememberOffsetOverscrollEffect()
            val overscrollEffect = rememberOffsetOverscrollEffect(placeRelatively = false)
            SwipeToReveal(
                foregroundContent = { PagerContent(overscrollEffect) },
                foregroundContentEffect = overscrollEffect,
+1 −1
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ fun SwipeToReveal(
    // This composable supports an overscroll effect, to make it possible for the user to
    // "stretch" the UI when the side is fully revealed but the user keeps trying to reveal it
    // further.
    val revealedContentEffect = rememberOffsetOverscrollEffect()
    val revealedContentEffect = rememberOffsetOverscrollEffect(placeRelatively = false)

    // This is the width of the revealed content UI box. It's not a state because it's not
    // observed in any composition and is an object with a value to avoid the extra cost