|
|
@ -16,8 +16,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
package com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar
|
|
|
|
package com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar
|
|
|
|
|
|
|
|
|
|
|
|
import androidx.compose.animation.core.animateDpAsState
|
|
|
|
|
|
|
|
import androidx.compose.foundation.gestures.Orientation
|
|
|
|
import androidx.compose.foundation.gestures.Orientation
|
|
|
|
|
|
|
|
import androidx.compose.foundation.gestures.Orientation.Horizontal
|
|
|
|
|
|
|
|
import androidx.compose.foundation.gestures.Orientation.Vertical
|
|
|
|
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
|
|
|
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
|
|
|
import androidx.compose.foundation.gestures.detectTapGestures
|
|
|
|
import androidx.compose.foundation.gestures.detectTapGestures
|
|
|
|
import androidx.compose.foundation.gestures.detectVerticalDragGestures
|
|
|
|
import androidx.compose.foundation.gestures.detectVerticalDragGestures
|
|
|
@ -28,31 +29,28 @@ import androidx.compose.foundation.interaction.PressInteraction
|
|
|
|
import androidx.compose.foundation.layout.Box
|
|
|
|
import androidx.compose.foundation.layout.Box
|
|
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
|
import androidx.compose.foundation.layout.height
|
|
|
|
|
|
|
|
import androidx.compose.foundation.layout.offset
|
|
|
|
|
|
|
|
import androidx.compose.foundation.layout.width
|
|
|
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.Immutable
|
|
|
|
import androidx.compose.runtime.Immutable
|
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.mutableFloatStateOf
|
|
|
|
import androidx.compose.runtime.mutableFloatStateOf
|
|
|
|
|
|
|
|
import androidx.compose.runtime.mutableLongStateOf
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.runtime.rememberUpdatedState
|
|
|
|
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.runtime.snapshotFlow
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.geometry.Offset
|
|
|
|
import androidx.compose.ui.geometry.Offset
|
|
|
|
import androidx.compose.ui.input.pointer.PointerInputChange
|
|
|
|
import androidx.compose.ui.input.pointer.PointerInputChange
|
|
|
|
import androidx.compose.ui.input.pointer.pointerInput
|
|
|
|
import androidx.compose.ui.input.pointer.pointerInput
|
|
|
|
|
|
|
|
import androidx.compose.ui.layout.Layout
|
|
|
|
|
|
|
|
import androidx.compose.ui.layout.layout
|
|
|
|
import androidx.compose.ui.layout.onGloballyPositioned
|
|
|
|
import androidx.compose.ui.layout.onGloballyPositioned
|
|
|
|
import androidx.compose.ui.layout.positionInRoot
|
|
|
|
import androidx.compose.ui.layout.positionInRoot
|
|
|
|
import androidx.compose.ui.platform.LocalDensity
|
|
|
|
|
|
|
|
import androidx.compose.ui.unit.Dp
|
|
|
|
import androidx.compose.ui.unit.Dp
|
|
|
|
import androidx.compose.ui.unit.IntOffset
|
|
|
|
import androidx.compose.ui.unit.IntOffset
|
|
|
|
import androidx.compose.ui.unit.IntSize
|
|
|
|
import androidx.compose.ui.unit.IntSize
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.max
|
|
|
|
|
|
|
|
import androidx.compose.ui.util.packFloats
|
|
|
|
import androidx.compose.ui.util.packFloats
|
|
|
|
import androidx.compose.ui.util.unpackFloat1
|
|
|
|
import androidx.compose.ui.util.unpackFloat1
|
|
|
|
import androidx.compose.ui.util.unpackFloat2
|
|
|
|
import androidx.compose.ui.util.unpackFloat2
|
|
|
@ -61,6 +59,7 @@ import kotlinx.coroutines.delay
|
|
|
|
import kotlinx.coroutines.withTimeout
|
|
|
|
import kotlinx.coroutines.withTimeout
|
|
|
|
import kotlin.math.max
|
|
|
|
import kotlin.math.max
|
|
|
|
import kotlin.math.min
|
|
|
|
import kotlin.math.min
|
|
|
|
|
|
|
|
import kotlin.math.roundToInt
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* The delay between scrolls when a user long presses on the scrollbar track to initiate a scroll
|
|
|
|
* The delay between scrolls when a user long presses on the scrollbar track to initiate a scroll
|
|
|
@ -74,21 +73,53 @@ private const val SCROLLBAR_PRESS_DELAY_MS = 10L
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private const val SCROLLBAR_PRESS_DELTA_PCT = 0.02f
|
|
|
|
private const val SCROLLBAR_PRESS_DELTA_PCT = 0.02f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScrollbarState {
|
|
|
|
|
|
|
|
private var packedValue by mutableLongStateOf(0L)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal fun onScroll(stateValue: ScrollbarStateValue) {
|
|
|
|
|
|
|
|
packedValue = stateValue.packedValue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the thumb size of the scrollbar as a percentage of the total track size
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
val thumbSizePercent
|
|
|
|
|
|
|
|
get() = unpackFloat1(packedValue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the distance the thumb has traveled as a percentage of total track size
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
val thumbMovedPercent
|
|
|
|
|
|
|
|
get() = unpackFloat2(packedValue)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the size of the scrollbar track in pixels
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private val ScrollbarTrack.size
|
|
|
|
|
|
|
|
get() = unpackFloat2(packedValue) - unpackFloat1(packedValue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the position of the scrollbar thumb on the track as a percentage
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private fun ScrollbarTrack.thumbPosition(
|
|
|
|
|
|
|
|
dimension: Float,
|
|
|
|
|
|
|
|
): Float = max(
|
|
|
|
|
|
|
|
a = min(
|
|
|
|
|
|
|
|
a = dimension / size,
|
|
|
|
|
|
|
|
b = 1f,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
b = 0f,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Class definition for the core properties of a scroll bar
|
|
|
|
* Class definition for the core properties of a scroll bar
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@Immutable
|
|
|
|
@Immutable
|
|
|
|
@JvmInline
|
|
|
|
@JvmInline
|
|
|
|
value class ScrollbarState internal constructor(
|
|
|
|
value class ScrollbarStateValue internal constructor(
|
|
|
|
internal val packedValue: Long,
|
|
|
|
internal val packedValue: Long,
|
|
|
|
) {
|
|
|
|
)
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
val FULL = ScrollbarState(
|
|
|
|
|
|
|
|
thumbSizePercent = 1f,
|
|
|
|
|
|
|
|
thumbMovedPercent = 0f,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Class definition for the core properties of a scroll bar track
|
|
|
|
* Class definition for the core properties of a scroll bar track
|
|
|
@ -105,54 +136,23 @@ private value class ScrollbarTrack(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Creates a [ScrollbarState] with the listed properties
|
|
|
|
* Creates a [ScrollbarStateValue] with the listed properties
|
|
|
|
* @param thumbSizePercent the thumb size of the scrollbar as a percentage of the total track size.
|
|
|
|
* @param thumbSizePercent the thumb size of the scrollbar as a percentage of the total track size.
|
|
|
|
* Refers to either the thumb width (for horizontal scrollbars)
|
|
|
|
* Refers to either the thumb width (for horizontal scrollbars)
|
|
|
|
* or height (for vertical scrollbars).
|
|
|
|
* or height (for vertical scrollbars).
|
|
|
|
* @param thumbMovedPercent the distance the thumb has traveled as a percentage of total
|
|
|
|
* @param thumbMovedPercent the distance the thumb has traveled as a percentage of total
|
|
|
|
* track size.
|
|
|
|
* track size.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
fun ScrollbarState(
|
|
|
|
fun scrollbarStateValue(
|
|
|
|
thumbSizePercent: Float,
|
|
|
|
thumbSizePercent: Float,
|
|
|
|
thumbMovedPercent: Float,
|
|
|
|
thumbMovedPercent: Float,
|
|
|
|
) = ScrollbarState(
|
|
|
|
) = ScrollbarStateValue(
|
|
|
|
packFloats(
|
|
|
|
packFloats(
|
|
|
|
val1 = thumbSizePercent,
|
|
|
|
val1 = thumbSizePercent,
|
|
|
|
val2 = thumbMovedPercent,
|
|
|
|
val2 = thumbMovedPercent,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the thumb size of the scrollbar as a percentage of the total track size
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
val ScrollbarState.thumbSizePercent
|
|
|
|
|
|
|
|
get() = unpackFloat1(packedValue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the distance the thumb has traveled as a percentage of total track size
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
val ScrollbarState.thumbMovedPercent
|
|
|
|
|
|
|
|
get() = unpackFloat2(packedValue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the size of the scrollbar track in pixels
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private val ScrollbarTrack.size
|
|
|
|
|
|
|
|
get() = unpackFloat2(packedValue) - unpackFloat1(packedValue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the position of the scrollbar thumb on the track as a percentage
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private fun ScrollbarTrack.thumbPosition(
|
|
|
|
|
|
|
|
dimension: Float,
|
|
|
|
|
|
|
|
): Float = max(
|
|
|
|
|
|
|
|
a = min(
|
|
|
|
|
|
|
|
a = dimension / size,
|
|
|
|
|
|
|
|
b = 1f,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
b = 0f,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Returns the value of [offset] along the axis specified by [this]
|
|
|
|
* Returns the value of [offset] along the axis specified by [this]
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -197,8 +197,6 @@ fun Scrollbar(
|
|
|
|
thumb: @Composable () -> Unit,
|
|
|
|
thumb: @Composable () -> Unit,
|
|
|
|
onThumbMoved: ((Float) -> Unit)? = null,
|
|
|
|
onThumbMoved: ((Float) -> Unit)? = null,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val localDensity = LocalDensity.current
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Using Offset.Unspecified and Float.NaN instead of null
|
|
|
|
// Using Offset.Unspecified and Float.NaN instead of null
|
|
|
|
// to prevent unnecessary boxing of primitives
|
|
|
|
// to prevent unnecessary boxing of primitives
|
|
|
|
var pressedOffset by remember { mutableStateOf(Offset.Unspecified) }
|
|
|
|
var pressedOffset by remember { mutableStateOf(Offset.Unspecified) }
|
|
|
@ -210,23 +208,6 @@ fun Scrollbar(
|
|
|
|
|
|
|
|
|
|
|
|
var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) }
|
|
|
|
var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) }
|
|
|
|
|
|
|
|
|
|
|
|
val thumbTravelPercent = when {
|
|
|
|
|
|
|
|
interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent
|
|
|
|
|
|
|
|
else -> interactionThumbTravelPercent
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
val thumbSizePx = max(
|
|
|
|
|
|
|
|
a = state.thumbSizePercent * track.size,
|
|
|
|
|
|
|
|
b = with(localDensity) { minThumbSize.toPx() },
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
val thumbSizeDp by animateDpAsState(
|
|
|
|
|
|
|
|
targetValue = with(localDensity) { thumbSizePx.toDp() },
|
|
|
|
|
|
|
|
label = "scrollbar thumb size",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
val thumbMovedPx = min(
|
|
|
|
|
|
|
|
a = track.size * thumbTravelPercent,
|
|
|
|
|
|
|
|
b = track.size - thumbSizePx,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// scrollbar track container
|
|
|
|
// scrollbar track container
|
|
|
|
Box(
|
|
|
|
Box(
|
|
|
|
modifier = modifier
|
|
|
|
modifier = modifier
|
|
|
@ -320,84 +301,110 @@ fun Scrollbar(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val scrollbarThumbMovedDp = max(
|
|
|
|
|
|
|
|
a = with(localDensity) { thumbMovedPx.toDp() },
|
|
|
|
|
|
|
|
b = 0.dp,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
// scrollbar thumb container
|
|
|
|
// scrollbar thumb container
|
|
|
|
Box(
|
|
|
|
Layout(content = { thumb() }) { measurables, constraints ->
|
|
|
|
modifier = Modifier
|
|
|
|
val measurable = measurables.first()
|
|
|
|
.align(Alignment.TopStart)
|
|
|
|
|
|
|
|
.run {
|
|
|
|
val thumbSizePx = max(
|
|
|
|
when (orientation) {
|
|
|
|
a = state.thumbSizePercent * track.size,
|
|
|
|
Orientation.Horizontal -> width(thumbSizeDp)
|
|
|
|
b = minThumbSize.toPx(),
|
|
|
|
Orientation.Vertical -> height(thumbSizeDp)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val thumbTravelPercent = when {
|
|
|
|
|
|
|
|
interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent
|
|
|
|
|
|
|
|
else -> interactionThumbTravelPercent
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val thumbMovedPx = min(
|
|
|
|
|
|
|
|
a = track.size * thumbTravelPercent,
|
|
|
|
|
|
|
|
b = track.size - thumbSizePx,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val scrollbarThumbMovedPx = max(
|
|
|
|
|
|
|
|
a = thumbMovedPx.roundToInt(),
|
|
|
|
|
|
|
|
b = 0,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val y = when (orientation) {
|
|
|
|
|
|
|
|
Horizontal -> 0
|
|
|
|
|
|
|
|
Vertical -> scrollbarThumbMovedPx
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
val x = when (orientation) {
|
|
|
|
|
|
|
|
Horizontal -> scrollbarThumbMovedPx
|
|
|
|
|
|
|
|
Vertical -> 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val updatedConstraints = when (orientation) {
|
|
|
|
|
|
|
|
Horizontal -> {
|
|
|
|
|
|
|
|
constraints.copy(
|
|
|
|
|
|
|
|
minWidth = thumbSizePx.roundToInt(),
|
|
|
|
|
|
|
|
maxWidth = thumbSizePx.roundToInt(),
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.offset(
|
|
|
|
Vertical -> {
|
|
|
|
y = when (orientation) {
|
|
|
|
constraints.copy(
|
|
|
|
Orientation.Horizontal -> 0.dp
|
|
|
|
minHeight = thumbSizePx.roundToInt(),
|
|
|
|
Orientation.Vertical -> scrollbarThumbMovedDp
|
|
|
|
maxHeight = thumbSizePx.roundToInt(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
x = when (orientation) {
|
|
|
|
}
|
|
|
|
Orientation.Horizontal -> scrollbarThumbMovedDp
|
|
|
|
}
|
|
|
|
Orientation.Vertical -> 0.dp
|
|
|
|
|
|
|
|
},
|
|
|
|
val placeable = measurable.measure(updatedConstraints)
|
|
|
|
),
|
|
|
|
layout(placeable.width, placeable.height) {
|
|
|
|
) {
|
|
|
|
placeable.place(x, y)
|
|
|
|
thumb()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (onThumbMoved == null) return
|
|
|
|
if (onThumbMoved == null) return
|
|
|
|
|
|
|
|
|
|
|
|
// State that will be read inside the effects that follow
|
|
|
|
|
|
|
|
// but will not cause re-triggering of them
|
|
|
|
|
|
|
|
val updatedState by rememberUpdatedState(state)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process presses
|
|
|
|
// Process presses
|
|
|
|
LaunchedEffect(pressedOffset) {
|
|
|
|
LaunchedEffect(Unit) {
|
|
|
|
// Press ended, reset interactionThumbTravelPercent
|
|
|
|
snapshotFlow { pressedOffset }.collect { pressedOffset ->
|
|
|
|
if (pressedOffset == Offset.Unspecified) {
|
|
|
|
// Press ended, reset interactionThumbTravelPercent
|
|
|
|
interactionThumbTravelPercent = Float.NaN
|
|
|
|
if (pressedOffset == Offset.Unspecified) {
|
|
|
|
return@LaunchedEffect
|
|
|
|
interactionThumbTravelPercent = Float.NaN
|
|
|
|
}
|
|
|
|
return@collect
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var currentThumbMovedPercent = updatedState.thumbMovedPercent
|
|
|
|
var currentThumbMovedPercent = state.thumbMovedPercent
|
|
|
|
val destinationThumbMovedPercent = track.thumbPosition(
|
|
|
|
val destinationThumbMovedPercent = track.thumbPosition(
|
|
|
|
dimension = orientation.valueOf(pressedOffset),
|
|
|
|
dimension = orientation.valueOf(pressedOffset),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
val isPositive = currentThumbMovedPercent < destinationThumbMovedPercent
|
|
|
|
val isPositive = currentThumbMovedPercent < destinationThumbMovedPercent
|
|
|
|
val delta = SCROLLBAR_PRESS_DELTA_PCT * if (isPositive) 1f else -1f
|
|
|
|
val delta = SCROLLBAR_PRESS_DELTA_PCT * if (isPositive) 1f else -1f
|
|
|
|
|
|
|
|
|
|
|
|
while (currentThumbMovedPercent != destinationThumbMovedPercent) {
|
|
|
|
while (currentThumbMovedPercent != destinationThumbMovedPercent) {
|
|
|
|
currentThumbMovedPercent = when {
|
|
|
|
currentThumbMovedPercent = when {
|
|
|
|
isPositive -> min(
|
|
|
|
isPositive -> min(
|
|
|
|
a = currentThumbMovedPercent + delta,
|
|
|
|
a = currentThumbMovedPercent + delta,
|
|
|
|
b = destinationThumbMovedPercent,
|
|
|
|
b = destinationThumbMovedPercent,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
else -> max(
|
|
|
|
else -> max(
|
|
|
|
a = currentThumbMovedPercent + delta,
|
|
|
|
a = currentThumbMovedPercent + delta,
|
|
|
|
b = destinationThumbMovedPercent,
|
|
|
|
b = destinationThumbMovedPercent,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
onThumbMoved(currentThumbMovedPercent)
|
|
|
|
|
|
|
|
interactionThumbTravelPercent = currentThumbMovedPercent
|
|
|
|
|
|
|
|
delay(SCROLLBAR_PRESS_DELAY_MS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
onThumbMoved(currentThumbMovedPercent)
|
|
|
|
|
|
|
|
interactionThumbTravelPercent = currentThumbMovedPercent
|
|
|
|
|
|
|
|
delay(SCROLLBAR_PRESS_DELAY_MS)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Process drags
|
|
|
|
// Process drags
|
|
|
|
LaunchedEffect(draggedOffset) {
|
|
|
|
LaunchedEffect(Unit) {
|
|
|
|
if (draggedOffset == Offset.Unspecified) {
|
|
|
|
snapshotFlow { draggedOffset }.collect { draggedOffset ->
|
|
|
|
interactionThumbTravelPercent = Float.NaN
|
|
|
|
if (draggedOffset == Offset.Unspecified) {
|
|
|
|
return@LaunchedEffect
|
|
|
|
interactionThumbTravelPercent = Float.NaN
|
|
|
|
|
|
|
|
return@collect
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
val currentTravel = track.thumbPosition(
|
|
|
|
|
|
|
|
dimension = orientation.valueOf(draggedOffset),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
onThumbMoved(currentTravel)
|
|
|
|
|
|
|
|
interactionThumbTravelPercent = currentTravel
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val currentTravel = track.thumbPosition(
|
|
|
|
|
|
|
|
dimension = orientation.valueOf(draggedOffset),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
onThumbMoved(currentTravel)
|
|
|
|
|
|
|
|
interactionThumbTravelPercent = currentTravel
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|