Skip to content

Commit

Permalink
Allow RSB scrolling with reversed direction (#1112)
Browse files Browse the repository at this point in the history
* Update RotaryVelocityTracker for using Impulse based velocity tracking
* Add ReverseDirection parameter into Rotary modifiers

---------

Co-authored-by: Yuri Schimke <[email protected]>
  • Loading branch information
Kpeved and yschimke authored Mar 16, 2023
1 parent ca1b5e9 commit 7560962
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 9 deletions.
8 changes: 4 additions & 4 deletions compose-layout/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,10 @@ package com.google.android.horologist.compose.rotaryinput {

public final class RotaryKt {
method @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static kotlinx.coroutines.flow.Flow<com.google.android.horologist.compose.rotaryinput.TimestampedDelta> batchRequestsWithinTimeframe(kotlinx.coroutines.flow.Flow<com.google.android.horologist.compose.rotaryinput.TimestampedDelta>, long timeframe);
method @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryHandler(androidx.compose.ui.Modifier, com.google.android.horologist.compose.rotaryinput.RotaryScrollHandler rotaryScrollHandler, optional long batchTimeframe, com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics);
method @androidx.compose.runtime.Composable @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryWithFling(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, androidx.compose.foundation.gestures.ScrollableState scrollableState, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics);
method @androidx.compose.runtime.Composable @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryWithScroll(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, androidx.compose.foundation.gestures.ScrollableState scrollableState, optional com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics);
method @androidx.compose.runtime.Composable @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryWithSnap(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, com.google.android.horologist.compose.rotaryinput.RotaryScrollAdapter rotaryScrollAdapter, optional com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics);
method @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryHandler(androidx.compose.ui.Modifier, com.google.android.horologist.compose.rotaryinput.RotaryScrollHandler rotaryScrollHandler, optional long batchTimeframe, boolean reverseDirection, com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics);
method @androidx.compose.runtime.Composable @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryWithFling(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, androidx.compose.foundation.gestures.ScrollableState scrollableState, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryWithScroll(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, androidx.compose.foundation.gestures.ScrollableState scrollableState, optional com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static androidx.compose.ui.Modifier rotaryWithSnap(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, com.google.android.horologist.compose.rotaryinput.RotaryScrollAdapter rotaryScrollAdapter, optional com.google.android.horologist.compose.rotaryinput.RotaryHapticFeedback rotaryHaptics, optional boolean reverseDirection);
method @com.google.android.horologist.compose.navscaffold.ExperimentalHorologistComposeLayoutApi public static com.google.android.horologist.compose.rotaryinput.RotaryScrollAdapter toRotaryScrollAdapter(androidx.wear.compose.foundation.lazy.ScalingLazyListState);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ private inline fun debugLog(generateMsg: () -> String) {
* @param scrollableState Scrollable state which will be scrolled while receiving rotary events
* @param flingBehavior Logic describing fling behavior.
* @param rotaryHaptics Class which will handle haptic feedback
* @param reverseDirection Reverse the direction of scrolling. Should be aligned with
* Scrollable `reverseDirection` parameter
*/
@ExperimentalHorologistComposeLayoutApi
@Suppress("ComposableModifierFactory")
Expand All @@ -106,9 +108,11 @@ public fun Modifier.rotaryWithFling(
focusRequester: FocusRequester,
scrollableState: ScrollableState,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
rotaryHaptics: RotaryHapticFeedback = rememberRotaryHapticFeedback()
rotaryHaptics: RotaryHapticFeedback = rememberRotaryHapticFeedback(),
reverseDirection: Boolean = false
): Modifier = rotaryHandler(
rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
reverseDirection = reverseDirection,
rotaryHaptics = rotaryHaptics
)
.focusRequester(focusRequester)
Expand All @@ -128,16 +132,20 @@ public fun Modifier.rotaryWithFling(
* @param focusRequester Requests the focus for rotary input
* @param scrollableState Scrollable state which will be scrolled while receiving rotary events
* @param rotaryHaptics Class which will handle haptic feedback
* @param reverseDirection Reverse the direction of scrolling. Should be aligned with
* Scrollable `reverseDirection` parameter
*/
@ExperimentalHorologistComposeLayoutApi
@Suppress("ComposableModifierFactory")
@Composable
public fun Modifier.rotaryWithScroll(
focusRequester: FocusRequester,
scrollableState: ScrollableState,
rotaryHaptics: RotaryHapticFeedback = rememberRotaryHapticFeedback()
rotaryHaptics: RotaryHapticFeedback = rememberRotaryHapticFeedback(),
reverseDirection: Boolean = false
): Modifier = rotaryHandler(
rotaryScrollHandler = RotaryDefaults.rememberFlingHandler(scrollableState, null),
reverseDirection = reverseDirection,
rotaryHaptics = rotaryHaptics
)
.focusRequester(focusRequester)
Expand All @@ -158,16 +166,20 @@ public fun Modifier.rotaryWithScroll(
* @param focusRequester Requests the focus for rotary input
* @param rotaryScrollAdapter A connection between scrollable objects and rotary events
* @param rotaryHaptics Class which will handle haptic feedback
* @param reverseDirection Reverse the direction of scrolling. Should be aligned with
* Scrollable `reverseDirection` parameter
*/
@ExperimentalHorologistComposeLayoutApi
@Suppress("ComposableModifierFactory")
@Composable
public fun Modifier.rotaryWithSnap(
focusRequester: FocusRequester,
rotaryScrollAdapter: RotaryScrollAdapter,
rotaryHaptics: RotaryHapticFeedback = rememberRotaryHapticFeedback()
rotaryHaptics: RotaryHapticFeedback = rememberRotaryHapticFeedback(),
reverseDirection: Boolean = false
): Modifier = rotaryHandler(
rotaryScrollHandler = RotaryDefaults.rememberSnapHandler(rotaryScrollAdapter),
reverseDirection = reverseDirection,
rotaryHaptics = rotaryHaptics
)
.focusRequester(focusRequester)
Expand Down Expand Up @@ -727,6 +739,7 @@ public class DefaultSnapBehavior(
public fun Modifier.rotaryHandler(
rotaryScrollHandler: RotaryScrollHandler,
batchTimeframe: Long = 0L,
reverseDirection: Boolean,
rotaryHaptics: RotaryHapticFeedback
): Modifier = composed {
val channel = rememberTimestampChannel()
Expand All @@ -751,7 +764,7 @@ public fun Modifier.rotaryHandler(
channel.trySend(
TimestampedDelta(
it.uptimeMillis,
it.verticalScrollPixels
it.verticalScrollPixels * sign(if (reverseDirection) -1f else 1f)
)
)
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ fun RotaryMenuScreen(
R.string.rotarymenu_scroll_list,
Screen.RotaryScrollScreen.route
),
Pair(
R.string.rotarymenu_scroll_list_reversed,
Screen.RotaryScrollReversedScreen.route
),
Pair(
R.string.rotarymenu_scroll_with_fling,
Screen.RotaryScrollWithFlingScreen.route
Expand Down Expand Up @@ -140,12 +144,18 @@ fun RotaryMenuScreen(

@Composable
fun RotaryScrollScreen(
reverseDirection: Boolean = false,
scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState(),
focusRequester: FocusRequester = rememberActiveFocusRequester()
) {
ItemsListWithModifier(
reverseDirection = reverseDirection,
modifier = Modifier
.rotaryWithScroll(focusRequester, scalingLazyListState),
.rotaryWithScroll(
reverseDirection = reverseDirection,
focusRequester = focusRequester,
scrollableState = scalingLazyListState
),
scrollableState = scalingLazyListState
) {
ChipsList {}
Expand Down Expand Up @@ -288,6 +298,7 @@ private fun ScrollPreferences(

@Composable
private fun ItemsListWithModifier(
reverseDirection: Boolean = false,
modifier: Modifier,
scrollableState: ScalingLazyListState,
items: ScalingLazyListScope.() -> Unit
Expand All @@ -296,6 +307,7 @@ private fun ItemsListWithModifier(
ScalingLazyColumn(
modifier = modifier.fillMaxSize(),
state = scrollableState,
reverseLayout = reverseDirection,
flingBehavior = flingBehavior,
scalingParams = scalingParams(),
horizontalAlignment = Alignment.Start,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ fun SampleWearApp() {
composable(route = Screen.RotaryScrollScreen.route) {
RotaryScrollScreen()
}
composable(route = Screen.RotaryScrollReversedScreen.route) {
RotaryScrollScreen(reverseDirection = true)
}
composable(route = Screen.RotaryScrollWithFlingScreen.route) {
RotaryScrollWithFlingOrSnapScreen(isFling = true, isSnap = false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ sealed class Screen(
object RotaryMenuScreen : Screen("rotaryMenuScreen")

object RotaryScrollScreen : Screen("rotaryScrollScreen")
object RotaryScrollReversedScreen : Screen("rotaryScrollReversedScreen")
object RotaryScrollWithFlingScreen : Screen("rotaryScrollWithFlingScreen")
object RotarySnapListScreen : Screen("rotarySnapListScreen")

Expand Down
1 change: 1 addition & 0 deletions sample/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

<string name="rotarymenu_scroll">Scroll</string>
<string name="rotarymenu_scroll_list">Scroll list</string>
<string name="rotarymenu_scroll_list_reversed">Reversed scroll list</string>
<string name="rotarymenu_scroll_with_fling">Scroll with fling</string>
<string name="rotarymenu_snap">Snap</string>
<string name="rotarymenu_snap_list">Snap list</string>
Expand Down

0 comments on commit 7560962

Please sign in to comment.