components: ReorderableColumn resists dragging past the ends
This commit is contained in:
+19
-1
@@ -84,6 +84,8 @@ fun <T> ReorderableColumn(
|
||||
// release settle — re-based onto the row's new slot once the order commits
|
||||
// (see onDragEnd), then eased down to zero.
|
||||
var dragOffset by remember { mutableFloatStateOf(0f) }
|
||||
// The finger's own travel; [dragOffset] is this held to the list's ends.
|
||||
var rawOffset by remember { mutableFloatStateOf(0f) }
|
||||
// The running release/cancel settle, cancelled if a new drag pre-empts it.
|
||||
// Purely visual — the order commit (see onDragEnd) never depends on it.
|
||||
var settleJob by remember { mutableStateOf<Job?>(null) }
|
||||
@@ -131,10 +133,13 @@ fun <T> ReorderableColumn(
|
||||
settleJob?.cancel()
|
||||
draggedKey = key
|
||||
dragOffset = 0f
|
||||
rawOffset = 0f
|
||||
},
|
||||
onDrag = { change, amount ->
|
||||
change.consume()
|
||||
dragOffset += amount.y
|
||||
rawOffset += amount.y
|
||||
val from = order.indexOfFirst { keyOf(it) == key }
|
||||
dragOffset = rubberBand(rawOffset, -from * pitchPx, (order.lastIndex - from) * pitchPx, pitchPx * 0.4f)
|
||||
},
|
||||
onDragEnd = {
|
||||
val from = order.indexOfFirst { keyOf(it) == key }
|
||||
@@ -206,3 +211,16 @@ private class InsetShape(private val base: Shape, private val inset: Dp) : Shape
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [value] as is between [min] and [max]; past either end it keeps following with
|
||||
* growing resistance and never gets further than [stretch] beyond it.
|
||||
*/
|
||||
private fun rubberBand(value: Float, min: Float, max: Float, stretch: Float): Float {
|
||||
fun resist(over: Float) = stretch * (1f - 1f / (over / stretch + 1f))
|
||||
return when {
|
||||
value > max -> max + resist(value - max)
|
||||
value < min -> min - resist(min - value)
|
||||
else -> value
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user