|
|
@ -80,7 +80,7 @@ value class ScrollbarState internal constructor(
|
|
|
|
companion object {
|
|
|
|
companion object {
|
|
|
|
val FULL = ScrollbarState(
|
|
|
|
val FULL = ScrollbarState(
|
|
|
|
thumbSizePercent = 1f,
|
|
|
|
thumbSizePercent = 1f,
|
|
|
|
thumbDisplacementPercent = 0f,
|
|
|
|
thumbMovedPercent = 0f,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -104,16 +104,16 @@ private value class ScrollbarTrack(
|
|
|
|
* @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 thumbDisplacementPercent 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 ScrollbarState(
|
|
|
|
thumbSizePercent: Float,
|
|
|
|
thumbSizePercent: Float,
|
|
|
|
thumbDisplacementPercent: Float,
|
|
|
|
thumbMovedPercent: Float,
|
|
|
|
) = ScrollbarState(
|
|
|
|
) = ScrollbarState(
|
|
|
|
packFloats(
|
|
|
|
packFloats(
|
|
|
|
val1 = thumbSizePercent,
|
|
|
|
val1 = thumbSizePercent,
|
|
|
|
val2 = thumbDisplacementPercent,
|
|
|
|
val2 = thumbMovedPercent,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
@ -126,7 +126,7 @@ val ScrollbarState.thumbSizePercent
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Returns the distance the thumb has traveled as a percentage of total track size
|
|
|
|
* Returns the distance the thumb has traveled as a percentage of total track size
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
val ScrollbarState.thumbDisplacementPercent
|
|
|
|
val ScrollbarState.thumbMovedPercent
|
|
|
|
get() = unpackFloat2(packedValue)
|
|
|
|
get() = unpackFloat2(packedValue)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -179,7 +179,7 @@ internal fun Orientation.valueOf(intOffset: IntOffset) = when (this) {
|
|
|
|
* @param minThumbSize the minimum size of the scrollbar thumb
|
|
|
|
* @param minThumbSize the minimum size of the scrollbar thumb
|
|
|
|
* @param interactionSource allows for observing the state of the scroll bar
|
|
|
|
* @param interactionSource allows for observing the state of the scroll bar
|
|
|
|
* @param thumb a composable for drawing the scrollbar thumb
|
|
|
|
* @param thumb a composable for drawing the scrollbar thumb
|
|
|
|
* @param onThumbDisplaced an function for reacting to scroll bar displacements caused by direct
|
|
|
|
* @param onThumbMoved an function for reacting to scroll bar displacements caused by direct
|
|
|
|
* interactions on the scrollbar thumb by the user, for example implementing a fast scroll
|
|
|
|
* interactions on the scrollbar thumb by the user, for example implementing a fast scroll
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
@ -190,7 +190,7 @@ fun Scrollbar(
|
|
|
|
minThumbSize: Dp = 40.dp,
|
|
|
|
minThumbSize: Dp = 40.dp,
|
|
|
|
interactionSource: MutableInteractionSource? = null,
|
|
|
|
interactionSource: MutableInteractionSource? = null,
|
|
|
|
thumb: @Composable () -> Unit,
|
|
|
|
thumb: @Composable () -> Unit,
|
|
|
|
onThumbDisplaced: ((Float) -> Unit)? = null,
|
|
|
|
onThumbMoved: ((Float) -> Unit)? = null,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val localDensity = LocalDensity.current
|
|
|
|
val localDensity = LocalDensity.current
|
|
|
|
|
|
|
|
|
|
|
@ -206,7 +206,7 @@ fun Scrollbar(
|
|
|
|
var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) }
|
|
|
|
var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) }
|
|
|
|
|
|
|
|
|
|
|
|
val thumbTravelPercent = when {
|
|
|
|
val thumbTravelPercent = when {
|
|
|
|
interactionThumbTravelPercent.isNaN() -> state.thumbDisplacementPercent
|
|
|
|
interactionThumbTravelPercent.isNaN() -> state.thumbMovedPercent
|
|
|
|
else -> interactionThumbTravelPercent
|
|
|
|
else -> interactionThumbTravelPercent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val thumbSizePx = max(
|
|
|
|
val thumbSizePx = max(
|
|
|
@ -217,7 +217,7 @@ fun Scrollbar(
|
|
|
|
targetValue = with(localDensity) { thumbSizePx.toDp() },
|
|
|
|
targetValue = with(localDensity) { thumbSizePx.toDp() },
|
|
|
|
label = "scrollbar thumb size",
|
|
|
|
label = "scrollbar thumb size",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
val thumbDisplacementPx = min(
|
|
|
|
val thumbMovedPx = min(
|
|
|
|
a = track.size * thumbTravelPercent,
|
|
|
|
a = track.size * thumbTravelPercent,
|
|
|
|
b = track.size - thumbSizePx,
|
|
|
|
b = track.size - thumbSizePx,
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -282,8 +282,8 @@ fun Scrollbar(
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val scrollbarThumbDisplacement = max(
|
|
|
|
val scrollbarThumbMovedDp = max(
|
|
|
|
a = with(localDensity) { thumbDisplacementPx.toDp() },
|
|
|
|
a = with(localDensity) { thumbMovedPx.toDp() },
|
|
|
|
b = 0.dp,
|
|
|
|
b = 0.dp,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
// scrollbar thumb container
|
|
|
|
// scrollbar thumb container
|
|
|
@ -299,10 +299,10 @@ fun Scrollbar(
|
|
|
|
.offset(
|
|
|
|
.offset(
|
|
|
|
y = when (orientation) {
|
|
|
|
y = when (orientation) {
|
|
|
|
Orientation.Horizontal -> 0.dp
|
|
|
|
Orientation.Horizontal -> 0.dp
|
|
|
|
Orientation.Vertical -> scrollbarThumbDisplacement
|
|
|
|
Orientation.Vertical -> scrollbarThumbMovedDp
|
|
|
|
},
|
|
|
|
},
|
|
|
|
x = when (orientation) {
|
|
|
|
x = when (orientation) {
|
|
|
|
Orientation.Horizontal -> scrollbarThumbDisplacement
|
|
|
|
Orientation.Horizontal -> scrollbarThumbMovedDp
|
|
|
|
Orientation.Vertical -> 0.dp
|
|
|
|
Orientation.Vertical -> 0.dp
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
@ -311,7 +311,7 @@ fun Scrollbar(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (onThumbDisplaced == null) return
|
|
|
|
if (onThumbMoved == null) return
|
|
|
|
|
|
|
|
|
|
|
|
// State that will be read inside the effects that follow
|
|
|
|
// State that will be read inside the effects that follow
|
|
|
|
// but will not cause re-triggering of them
|
|
|
|
// but will not cause re-triggering of them
|
|
|
@ -325,26 +325,26 @@ fun Scrollbar(
|
|
|
|
return@LaunchedEffect
|
|
|
|
return@LaunchedEffect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var currentThumbDisplacement = updatedState.thumbDisplacementPercent
|
|
|
|
var currentThumbMovedPercent = updatedState.thumbMovedPercent
|
|
|
|
val destinationThumbDisplacement = track.thumbPosition(
|
|
|
|
val destinationThumbMovedPercent = track.thumbPosition(
|
|
|
|
dimension = orientation.valueOf(pressedOffset),
|
|
|
|
dimension = orientation.valueOf(pressedOffset),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
val isPositive = currentThumbDisplacement < destinationThumbDisplacement
|
|
|
|
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 (currentThumbDisplacement != destinationThumbDisplacement) {
|
|
|
|
while (currentThumbMovedPercent != destinationThumbMovedPercent) {
|
|
|
|
currentThumbDisplacement = when {
|
|
|
|
currentThumbMovedPercent = when {
|
|
|
|
isPositive -> min(
|
|
|
|
isPositive -> min(
|
|
|
|
a = currentThumbDisplacement + delta,
|
|
|
|
a = currentThumbMovedPercent + delta,
|
|
|
|
b = destinationThumbDisplacement,
|
|
|
|
b = destinationThumbMovedPercent,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
else -> max(
|
|
|
|
else -> max(
|
|
|
|
a = currentThumbDisplacement + delta,
|
|
|
|
a = currentThumbMovedPercent + delta,
|
|
|
|
b = destinationThumbDisplacement,
|
|
|
|
b = destinationThumbMovedPercent,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
onThumbDisplaced(currentThumbDisplacement)
|
|
|
|
onThumbMoved(currentThumbMovedPercent)
|
|
|
|
interactionThumbTravelPercent = currentThumbDisplacement
|
|
|
|
interactionThumbTravelPercent = currentThumbMovedPercent
|
|
|
|
delay(SCROLLBAR_PRESS_DELAY_MS)
|
|
|
|
delay(SCROLLBAR_PRESS_DELAY_MS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -358,7 +358,7 @@ fun Scrollbar(
|
|
|
|
val currentTravel = track.thumbPosition(
|
|
|
|
val currentTravel = track.thumbPosition(
|
|
|
|
dimension = orientation.valueOf(draggedOffset),
|
|
|
|
dimension = orientation.valueOf(draggedOffset),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
onThumbDisplaced(currentTravel)
|
|
|
|
onThumbMoved(currentTravel)
|
|
|
|
interactionThumbTravelPercent = currentTravel
|
|
|
|
interactionThumbTravelPercent = currentTravel
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|