@@ -18,10 +18,11 @@ package com.google.samples.apps.nowinandroid.core.designsystem.component.scrollb
1818
1919import androidx.compose.animation.core.animateDpAsState
2020import androidx.compose.foundation.gestures.Orientation
21+ import androidx.compose.foundation.gestures.detectHorizontalDragGestures
2122import androidx.compose.foundation.gestures.detectTapGestures
22- import androidx.compose.foundation.gestures.draggable
23- import androidx.compose.foundation.gestures.rememberDraggableState
23+ import androidx.compose.foundation.gestures.detectVerticalDragGestures
2424import androidx.compose.foundation.hoverable
25+ import androidx.compose.foundation.interaction.DragInteraction
2526import androidx.compose.foundation.interaction.MutableInteractionSource
2627import androidx.compose.foundation.interaction.PressInteraction
2728import androidx.compose.foundation.layout.Box
@@ -41,6 +42,7 @@ import androidx.compose.runtime.setValue
4142import androidx.compose.ui.Alignment
4243import androidx.compose.ui.Modifier
4344import androidx.compose.ui.geometry.Offset
45+ import androidx.compose.ui.input.pointer.PointerInputChange
4446import androidx.compose.ui.input.pointer.pointerInput
4547import androidx.compose.ui.layout.onGloballyPositioned
4648import androidx.compose.ui.layout.positionInRoot
@@ -53,7 +55,9 @@ import androidx.compose.ui.unit.max
5355import androidx.compose.ui.util.packFloats
5456import androidx.compose.ui.util.unpackFloat1
5557import androidx.compose.ui.util.unpackFloat2
58+ import kotlinx.coroutines.TimeoutCancellationException
5659import kotlinx.coroutines.delay
60+ import kotlinx.coroutines.withTimeout
5761import kotlin.math.max
5862import kotlin.math.min
5963
@@ -221,14 +225,6 @@ fun Scrollbar(
221225 a = track.size * thumbTravelPercent,
222226 b = track.size - thumbSizePx,
223227 )
224- val draggableState = rememberDraggableState { delta ->
225- if (draggedOffset == Offset .Unspecified ) return @rememberDraggableState
226-
227- draggedOffset = when (orientation) {
228- Orientation .Vertical -> draggedOffset.copy(y = draggedOffset.y + delta)
229- Orientation .Horizontal -> draggedOffset.copy(x = draggedOffset.x + delta)
230- }
231- }
232228
233229 // scrollbar track container
234230 Box (
@@ -251,36 +247,77 @@ fun Scrollbar(
251247 .pointerInput(Unit ) {
252248 detectTapGestures(
253249 onPress = { offset ->
254- val initialPress = PressInteraction .Press (offset)
255- interactionSource?.tryEmit(initialPress)
256-
257- // Start the press
258- pressedOffset = offset
250+ try {
251+ // Wait for a long press before scrolling
252+ withTimeout(viewConfiguration.longPressTimeoutMillis) {
253+ tryAwaitRelease()
254+ }
255+ } catch (e: TimeoutCancellationException ) {
256+ // Start the press triggered scroll
257+ val initialPress = PressInteraction .Press (offset)
258+ interactionSource?.tryEmit(initialPress)
259259
260- interactionSource?.tryEmit(
261- when {
262- tryAwaitRelease() -> PressInteraction .Release (initialPress)
263- else -> PressInteraction .Cancel (initialPress)
264- },
265- )
260+ pressedOffset = offset
261+ interactionSource?.tryEmit(
262+ when {
263+ tryAwaitRelease() -> PressInteraction .Release (initialPress)
264+ else -> PressInteraction .Cancel (initialPress)
265+ },
266+ )
266267
267- // End the press
268- pressedOffset = Offset .Unspecified
268+ // End the press
269+ pressedOffset = Offset .Unspecified
270+ }
269271 },
270272 )
271273 }
272274 // Process scrollbar drags
273- .draggable(
274- state = draggableState,
275- orientation = orientation,
276- interactionSource = interactionSource,
277- onDragStarted = { startedPosition: Offset ->
278- draggedOffset = startedPosition
279- },
280- onDragStopped = {
275+ .pointerInput(Unit ) {
276+ var dragInteraction: DragInteraction .Start ? = null
277+ val onDragStart: (Offset ) -> Unit = { offset ->
278+ val start = DragInteraction .Start ()
279+ dragInteraction = start
280+ interactionSource?.tryEmit(start)
281+ draggedOffset = offset
282+ }
283+ val onDragEnd: () -> Unit = {
284+ dragInteraction?.let { interactionSource?.tryEmit(DragInteraction .Stop (it)) }
285+ draggedOffset = Offset .Unspecified
286+ }
287+ val onDragCancel: () -> Unit = {
288+ dragInteraction?.let { interactionSource?.tryEmit(DragInteraction .Cancel (it)) }
281289 draggedOffset = Offset .Unspecified
282- },
283- ),
290+ }
291+ val onDrag: (change: PointerInputChange , dragAmount: Float ) -> Unit =
292+ onDrag@{ _, delta ->
293+ if (draggedOffset == Offset .Unspecified ) return @onDrag
294+ draggedOffset = when (orientation) {
295+ Orientation .Vertical -> draggedOffset.copy(
296+ y = draggedOffset.y + delta,
297+ )
298+
299+ Orientation .Horizontal -> draggedOffset.copy(
300+ x = draggedOffset.x + delta,
301+ )
302+ }
303+ }
304+
305+ when (orientation) {
306+ Orientation .Horizontal -> detectHorizontalDragGestures(
307+ onDragStart = onDragStart,
308+ onDragEnd = onDragEnd,
309+ onDragCancel = onDragCancel,
310+ onHorizontalDrag = onDrag,
311+ )
312+
313+ Orientation .Vertical -> detectVerticalDragGestures(
314+ onDragStart = onDragStart,
315+ onDragEnd = onDragEnd,
316+ onDragCancel = onDragCancel,
317+ onVerticalDrag = onDrag,
318+ )
319+ }
320+ },
284321 ) {
285322 val scrollbarThumbMovedDp = max(
286323 a = with (localDensity) { thumbMovedPx.toDp() },
@@ -338,6 +375,7 @@ fun Scrollbar(
338375 a = currentThumbMovedPercent + delta,
339376 b = destinationThumbMovedPercent,
340377 )
378+
341379 else -> max(
342380 a = currentThumbMovedPercent + delta,
343381 b = destinationThumbMovedPercent,
0 commit comments