From 3134098965c5fa74c3201c5f920c04b06289a557 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 2 Aug 2026 20:55:57 +0200 Subject: [PATCH] feat(components): let a grouped row take a long-press MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GroupedSurface/GroupedRow gain onLongClick (with the platform long-press haptic), for lists that enter a selection mode from a row — Calendula's search results (#80). --- .../floret/components/GroupedList.kt | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt b/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt index 0cb7535..1897556 100644 --- a/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt +++ b/components/src/main/kotlin/de/jeanlucmakiola/floret/components/GroupedList.kt @@ -1,6 +1,8 @@ package de.jeanlucmakiola.floret.components import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.fillMaxWidth @@ -13,12 +15,15 @@ import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -65,13 +70,17 @@ fun groupedShape(position: Position, full: Dp, small: Dp): Shape = when (positio * Set [gapBelow] to false to suppress that 2dp separation — for a caller that * owns uniform spacing itself, e.g. [ReorderableColumn], where every slot must * share the same pitch for the drag maths to line up. + * + * [onLongClick] adds a long-press to the same surface (with the platform's + * long-press haptic), for lists that enter a selection mode from a row. */ -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun GroupedSurface( position: Position, modifier: Modifier = Modifier, onClick: (() -> Unit)? = null, + onLongClick: (() -> Unit)? = null, color: Color = MaterialTheme.colorScheme.surfaceContainerHigh, gapBelow: Boolean = true, content: @Composable () -> Unit, @@ -87,10 +96,24 @@ fun GroupedSurface( else -> Modifier } val base = modifier.fillMaxWidth().then(gap) - if (onClick != null) { - Surface(onClick = onClick, color = color, shape = shape, interactionSource = interaction, modifier = base) { content() } - } else { - Surface(color = color, shape = shape, modifier = base) { content() } + val haptics = LocalHapticFeedback.current + when { + onLongClick != null -> Surface( + color = color, + shape = shape, + modifier = base.combinedClickable( + interactionSource = interaction, + indication = ripple(), + onClick = { onClick?.invoke() }, + onLongClick = { + haptics.performHapticFeedback(HapticFeedbackType.LongPress) + onLongClick() + }, + ), + ) { content() } + onClick != null -> + Surface(onClick = onClick, color = color, shape = shape, interactionSource = interaction, modifier = base) { content() } + else -> Surface(color = color, shape = shape, modifier = base) { content() } } } @@ -105,6 +128,7 @@ fun GroupedSurface( * [container] overrides the row's tonal colour (e.g. a category tint); ignored * when [selected]. [gapBelow] (see [GroupedSurface]) suppresses the inter-row * gap for a caller that owns its own uniform spacing, e.g. [ReorderableColumn]. + * [onLongClick] (see [GroupedSurface]) adds a long-press to the row. */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -121,6 +145,7 @@ fun GroupedRow( leading: @Composable (() -> Unit)? = null, trailing: @Composable (() -> Unit)? = null, onClick: (() -> Unit)? = null, + onLongClick: (() -> Unit)? = null, ) { val itemColors = if (selected) { ListItemDefaults.colors( @@ -152,6 +177,7 @@ fun GroupedRow( position = position, modifier = modifier.padding(horizontal = GroupedListInset), onClick = onClick, + onLongClick = onLongClick, color = containerColor, gapBelow = gapBelow, ) {