aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2021-03-16 15:22:44 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-03-16 15:22:44 +0000
commit92d5b6729ff33e3557ca2daed9ed8b96c8c0d636 (patch)
tree36043d0680657c7e3310d503de6c96aed3bdf0bb
parent58fe84235c628be3a321e5e1275d15d0a344518a (diff)
parent95605fcdbca4aec2ad511d3ebe5261f3e8c9e4e5 (diff)
downloadsupport-92d5b6729ff33e3557ca2daed9ed8b96c8c0d636.tar.gz
Merge "Properly reverse velocity and deltas for flingbehaviour when reverseDirection=true" into androidx-main
-rw-r--r--compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt266
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt4
2 files changed, 268 insertions, 2 deletions
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index ab1d15d7898..bcc20f7b132 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -149,6 +149,61 @@ class ScrollableTest {
@Test
@OptIn(ExperimentalTestApi::class)
+ fun scrollable_horizontalScroll_reverse() = runBlockingWithManualClock { clock ->
+ var total = 0f
+ val controller = ScrollableState(
+ consumeScrollDelta = {
+ total += it
+ it
+ }
+ )
+ setScrollableContent {
+ Modifier.scrollable(
+ reverseDirection = true,
+ state = controller,
+ orientation = Orientation.Horizontal
+ )
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ this.swipe(
+ start = this.center,
+ end = Offset(this.center.x + 100f, this.center.y),
+ durationMillis = 100
+ )
+ }
+ advanceClockWhileAwaitersExist(clock)
+
+ val lastTotal = rule.runOnIdle {
+ assertThat(total).isLessThan(0)
+ total
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ this.swipe(
+ start = this.center,
+ end = Offset(this.center.x, this.center.y + 100f),
+ durationMillis = 100
+ )
+ }
+ advanceClockWhileAwaitersExist(clock)
+
+ rule.runOnIdle {
+ assertThat(total).isEqualTo(lastTotal)
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ this.swipe(
+ start = this.center,
+ end = Offset(this.center.x - 100f, this.center.y),
+ durationMillis = 100
+ )
+ }
+ advanceClockWhileAwaitersExist(clock)
+ rule.runOnIdle {
+ assertThat(total).isLessThan(0.01f)
+ }
+ }
+
+ @Test
+ @OptIn(ExperimentalTestApi::class)
fun scrollable_verticalScroll() = runBlockingWithManualClock { clock ->
var total = 0f
val controller = ScrollableState(
@@ -203,6 +258,61 @@ class ScrollableTest {
@Test
@OptIn(ExperimentalTestApi::class)
+ fun scrollable_verticalScroll_reversed() = runBlockingWithManualClock { clock ->
+ var total = 0f
+ val controller = ScrollableState(
+ consumeScrollDelta = {
+ total += it
+ it
+ }
+ )
+ setScrollableContent {
+ Modifier.scrollable(
+ reverseDirection = true,
+ state = controller,
+ orientation = Orientation.Vertical
+ )
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ this.swipe(
+ start = this.center,
+ end = Offset(this.center.x, this.center.y + 100f),
+ durationMillis = 100
+ )
+ }
+ advanceClockWhileAwaitersExist(clock)
+
+ val lastTotal = rule.runOnIdle {
+ assertThat(total).isLessThan(0)
+ total
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ this.swipe(
+ start = this.center,
+ end = Offset(this.center.x + 100f, this.center.y),
+ durationMillis = 100
+ )
+ }
+ advanceClockWhileAwaitersExist(clock)
+
+ rule.runOnIdle {
+ assertThat(total).isEqualTo(lastTotal)
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ this.swipe(
+ start = this.center,
+ end = Offset(this.center.x, this.center.y - 100f),
+ durationMillis = 100
+ )
+ }
+ advanceClockWhileAwaitersExist(clock)
+ rule.runOnIdle {
+ assertThat(total).isLessThan(0.01f)
+ }
+ }
+
+ @Test
+ @OptIn(ExperimentalTestApi::class)
fun scrollable_disabledWontCallLambda() = runBlockingWithManualClock { clock ->
val enabled = mutableStateOf(true)
var total = 0f
@@ -910,6 +1020,162 @@ class ScrollableTest {
}
@Test
+ fun scrollable_flingBehaviourCalled() {
+ var total = 0f
+ val controller = ScrollableState(
+ consumeScrollDelta = {
+ total += it
+ it
+ }
+ )
+ var flingCalled = 0
+ var flingVelocity: Float = Float.MAX_VALUE
+ val flingBehaviour = object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ flingCalled++
+ flingVelocity = initialVelocity
+ return 0f
+ }
+ }
+ setScrollableContent {
+ Modifier.scrollable(
+ state = controller,
+ flingBehavior = flingBehaviour,
+ orientation = Orientation.Horizontal
+ )
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ swipeWithVelocity(
+ this.center,
+ this.center + Offset(115f, 0f),
+ endVelocity = 1000f
+ )
+ }
+ assertThat(flingCalled).isEqualTo(1)
+ assertThat(flingVelocity).isWithin(5f).of(1000f)
+ }
+
+ @Test
+ fun scrollable_flingBehaviourCalled_reversed() {
+ var total = 0f
+ val controller = ScrollableState(
+ consumeScrollDelta = {
+ total += it
+ it
+ }
+ )
+ var flingCalled = 0
+ var flingVelocity: Float = Float.MAX_VALUE
+ val flingBehaviour = object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ flingCalled++
+ flingVelocity = initialVelocity
+ return 0f
+ }
+ }
+ setScrollableContent {
+ Modifier.scrollable(
+ state = controller,
+ reverseDirection = true,
+ flingBehavior = flingBehaviour,
+ orientation = Orientation.Horizontal
+ )
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ swipeWithVelocity(
+ this.center,
+ this.center + Offset(115f, 0f),
+ endVelocity = 1000f
+ )
+ }
+ assertThat(flingCalled).isEqualTo(1)
+ assertThat(flingVelocity).isWithin(5f).of(-1000f)
+ }
+
+ @Test
+ fun scrollable_flingBehaviourCalled_correctScope() {
+ var total = 0f
+ val controller = ScrollableState(
+ consumeScrollDelta = {
+ total += it
+ it
+ }
+ )
+ val flingBehaviour = object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ scrollBy(123f)
+ return 0f
+ }
+ }
+ setScrollableContent {
+ Modifier.scrollable(
+ state = controller,
+ flingBehavior = flingBehaviour,
+ orientation = Orientation.Horizontal
+ )
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ down(center)
+ moveBy(Offset(x = 100f, y = 0f))
+ }
+
+ val prevTotal = rule.runOnIdle {
+ assertThat(total).isGreaterThan(0f)
+ total
+ }
+
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ up()
+ }
+
+ rule.runOnIdle {
+ assertThat(total).isEqualTo(prevTotal + 123)
+ }
+ }
+
+ @Test
+ fun scrollable_flingBehaviourCalled_reversed_correctScope() {
+ var total = 0f
+ val controller = ScrollableState(
+ consumeScrollDelta = {
+ total += it
+ it
+ }
+ )
+ val flingBehaviour = object : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ scrollBy(123f)
+ return 0f
+ }
+ }
+ setScrollableContent {
+ Modifier.scrollable(
+ state = controller,
+ reverseDirection = true,
+ flingBehavior = flingBehaviour,
+ orientation = Orientation.Horizontal
+ )
+ }
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ down(center)
+ moveBy(Offset(x = 100f, y = 0f))
+ }
+
+ val prevTotal = rule.runOnIdle {
+ assertThat(total).isLessThan(0f)
+ total
+ }
+
+ rule.onNodeWithTag(scrollableBoxTag).performGesture {
+ up()
+ }
+
+ rule.runOnIdle {
+ assertThat(total).isEqualTo(prevTotal + 123)
+ }
+ }
+
+ @Test
fun testInspectorValue() {
val controller = ScrollableState(
consumeScrollDelta = { it }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index 4b3d18af152..21b9c10051a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -212,7 +212,7 @@ private class ScrollingLogic(
// come up with the better threshold, but we need it since spline curve gives us NaNs
scrollableState.scroll {
val outerScopeScroll: (Float) -> Float =
- { delta -> this.dispatchScroll(delta, NestedScrollSource.Fling) }
+ { delta -> this.dispatchScroll(delta.reverseIfNeeded(), NestedScrollSource.Fling) }
val scope = object : ScrollScope {
override fun scrollBy(pixels: Float): Float {
return outerScopeScroll.invoke(pixels)
@@ -220,7 +220,7 @@ private class ScrollingLogic(
}
with(scope) {
with(flingBehavior) {
- result = performFling(available.toFloat()).toVelocity()
+ result = performFling(available.toFloat().reverseIfNeeded()).toVelocity()
}
}
}