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

Commit 6b7574f3 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Add NestedDraggable.shouldConsumeNestedPreScroll()

This CL adds a shouldConsumeNestedPreScroll() callback to
NestedDraggable so that it can consume scroll events before nested
scrollable.

This CL also renames shouldConsumedNestedScroll() to
shouldConsumedNestedPostScroll(), to make it more explicit when it is
called.

Bug: 397989775
Test: atest NestedDraggableTest
Flag: EXEMPT new unused API
Change-Id: I63b7f3295e22aa0c75f1648d6c2ee409a763bcbd
parent c6ad7cd0
Loading
Loading
Loading
Loading
+44 −20
Original line number Diff line number Diff line
@@ -95,10 +95,20 @@ interface NestedDraggable {
     * nested scrollable.
     *
     * This is called whenever a nested scrollable does not consume some scroll amount. If this
     * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
     * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
     * consume all future events during preScroll until the nested scroll is finished.
     */
    fun shouldConsumeNestedScroll(sign: Float): Boolean
    fun shouldConsumeNestedPostScroll(sign: Float): Boolean = true

    /**
     * Whether this draggable should consume any scroll amount with the given [sign] *before* it can
     * be consumed by a nested scrollable.
     *
     * This is called before a nested scrollable is able to consume that scroll amount. If this
     * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
     * consume all future scroll events during preScroll until the nested scroll is finished.
     */
    fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false

    interface Controller {
        /**
@@ -540,6 +550,14 @@ private class NestedDraggableNode(
    }

    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
        val sign = available.toFloat().sign
        maybeCreateNewController(
            sign = sign,
            condition = {
                source == NestedScrollSource.UserInput &&
                    draggable.shouldConsumeNestedPreScroll(sign)
            },
        )
        val controller = nestedScrollController ?: return Offset.Zero
        return scrollWithOverscroll(controller, available)
    }
@@ -560,19 +578,29 @@ private class NestedDraggableNode(
        }

        val sign = offset.sign
        maybeCreateNewController(
            sign,
            condition = { draggable.shouldConsumeNestedPostScroll(sign) },
        )
        val controller = nestedScrollController ?: return Offset.Zero
        return scrollWithOverscroll(controller, available)
    }

    private fun maybeCreateNewController(sign: Float, condition: () -> Boolean) {
        if (
            nestedDragsEnabled &&
                nestedScrollController == null &&
                // TODO(b/388231324): Remove this.
                !lastEventWasScrollWheel &&
                draggable.shouldConsumeNestedScroll(sign) &&
                lastFirstDown != null
            !nestedDragsEnabled ||
                nestedScrollController != null ||
                lastEventWasScrollWheel ||
                lastFirstDown == null ||
                !condition()
        ) {
            val startedPosition = checkNotNull(lastFirstDown)
            return
        }

        // TODO(b/382665591): Ensure that there is at least one pointer down.
        val pointersDownCount = pointersDown.size.coerceAtLeast(1)
        val pointerType = pointersDown.entries.firstOrNull()?.value
        val startedPosition = checkNotNull(lastFirstDown)
        nestedScrollController =
            NestedScrollController(
                overscrollEffect,
@@ -580,10 +608,6 @@ private class NestedDraggableNode(
            )
    }

        val controller = nestedScrollController ?: return Offset.Zero
        return scrollWithOverscroll(controller, available)
    }

    private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
        return scrollWithOverscroll(offset) {
            controller.controller.onDrag(it.toFloat()).toOffset()
+37 −3
Original line number Diff line number Diff line
@@ -971,6 +971,35 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
        assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
    }

    @Test
    fun consumeNestedPreScroll() {
        var consumeNestedPreScroll by mutableStateOf(false)
        val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })

        val touchSlop =
            rule.setContentWithTouchSlop {
                Box(
                    Modifier.fillMaxSize()
                        .nestedDraggable(draggable, orientation)
                        // Always consume everything so that the only way to start the drag is to
                        // intercept preScroll events.
                        .scrollable(rememberScrollableState { it }, orientation)
                )
            }

        rule.onRoot().performTouchInput {
            down(center)
            moveBy((touchSlop + 1f).toOffset())
        }

        assertThat(draggable.onDragStartedCalled).isFalse()

        consumeNestedPreScroll = true
        rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }

        assertThat(draggable.onDragStartedCalled).isTrue()
    }

    private fun ComposeContentTestRule.setContentWithTouchSlop(
        content: @Composable () -> Unit
    ): Float {
@@ -996,7 +1025,8 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
            { velocity, _ ->
                velocity
            },
        private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
        private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
        private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
    ) : NestedDraggable {
        var shouldStartDrag = true
        var onDragStartedCalled = false
@@ -1042,8 +1072,12 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
            }
        }

        override fun shouldConsumeNestedScroll(sign: Float): Boolean {
            return shouldConsumeNestedScroll.invoke(sign)
        override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
            return shouldConsumeNestedPostScroll.invoke(sign)
        }

        override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
            return shouldConsumeNestedPreScroll.invoke(sign)
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ internal class DraggableHandler(
        return layoutImpl.swipeDetector.detectSwipe(change)
    }

    override fun shouldConsumeNestedScroll(sign: Float): Boolean {
    override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
        return this.enabled()
    }