|
@Composable |
|
fun RepeatingButton( |
|
modifier: Modifier = Modifier, |
|
onClick: () -> Unit, |
|
enabled: Boolean = true, |
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, |
|
elevation: ButtonElevation? = ButtonDefaults.elevation(), |
|
shape: Shape = MaterialTheme.shapes.small, |
|
border: BorderStroke? = null, |
|
colors: ButtonColors = ButtonDefaults.buttonColors(), |
|
contentPadding: PaddingValues = ButtonDefaults.ContentPadding, |
|
maxDelayMillis: Long = 1000, |
|
minDelayMillis: Long = 5, |
|
delayDecayFactor: Float = .15f, |
|
content: @Composable RowScope.() -> Unit |
|
) { |
|
|
|
val currentClickListener by rememberUpdatedState(onClick) |
|
val scope = rememberCoroutineScope() |
|
|
|
Button( |
|
modifier = modifier.pointerInput(interactionSource, enabled) { |
|
scope.launch { |
|
awaitEachGesture { |
|
val down = awaitFirstDown(requireUnconsumed = false) |
|
|
|
val heldButtonJob = launch { |
|
var currentDelayMillis = maxDelayMillis |
|
while (enabled && down.pressed) { |
|
currentClickListener() |
|
delay(currentDelayMillis) |
|
val nextDelayMillis = |
|
currentDelayMillis - (currentDelayMillis * delayDecayFactor) |
|
currentDelayMillis = |
|
nextDelayMillis.toLong().coerceAtLeast(minDelayMillis) |
|
} |
|
} |
|
|
|
waitForUpOrCancellation() |
|
heldButtonJob.cancel() |
|
} |
|
} |
|
}, |
|
onClick = {}, |
|
enabled = enabled, |
|
interactionSource = interactionSource, |
|
elevation = elevation, |
|
shape = shape, |
|
border = border, |
|
colors = colors, |
|
contentPadding = contentPadding, |
|
content = content |
|
) |
|
} |