diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt index 36a6b64..118e514 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt @@ -1,6 +1,7 @@ package com.jetpackduba.gitnuro.extensions import androidx.compose.foundation.* +import androidx.compose.foundation.gestures.* import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -10,6 +11,7 @@ import androidx.compose.ui.awt.awtEventOrNull import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.pointer.* +import kotlinx.coroutines.coroutineScope fun Modifier.backgroundIf(condition: Boolean, color: Color, elseColor: Color? = null): Modifier { return if (condition) { @@ -52,6 +54,45 @@ fun Modifier.handOnHover(): Modifier { return this.pointerHoverIcon(PointerIcon.Hand) } +/** + * Detects double clicks without messing with other [clickable] features (like hover effect, single-click duration and + * accessibility features). Doesn't work when combined with long press though. This is a simplified version of + * [PointerInputScope.detectTapGestures] for double clicks only, and without consuming the first click. + */ +@Composable +fun Modifier.onDoubleClick( + onDoubleClick: () -> Unit, +): Modifier { + return this.pointerInput(Unit) { + coroutineScope { + forEachGesture { + awaitPointerEventScope { + // Detect first click without consuming it (other, independent handlers want it). + awaitFirstDown() + val firstUp = waitForUpOrCancellation() ?: return@awaitPointerEventScope + + // Detect and consume the second click if it's received within the timeout. + val secondDown = withTimeoutOrNull(viewConfiguration.doubleTapTimeoutMillis) { + val minUptime = firstUp.uptimeMillis + viewConfiguration.doubleTapMinTimeMillis + var change: PointerInputChange + // The second tap doesn't count if it happens before DoubleTapMinTime of the first tap + do { + change = awaitFirstDown() + } while (change.uptimeMillis < minUptime) + change + } ?: return@awaitPointerEventScope + secondDown.consume() + val secondUp = waitForUpOrCancellation() ?: return@awaitPointerEventScope + secondUp.consume() + + // Both clicks happened in time, fire the event. + onDoubleClick() + } + } + } + } +} + // TODO Try to restore hover that was shown with clickable modifier @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt index 058c719..2114c4a 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt @@ -716,6 +716,7 @@ private fun FileEntry( Box( modifier = Modifier .handMouseClickable { onClick() } + .onDoubleClick(onButtonClick) .fillMaxWidth() .hoverable(hoverInteraction) ) {