Make bubble scale when pressing on empty space

Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
2026-04-03 16:09:38 +03:00
Unverified
parent 9a5acbe810
commit 58dc1223c2
@@ -44,6 +44,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@@ -101,6 +102,7 @@ fun MessageItem(
var isPressed by remember { mutableStateOf(false) } var isPressed by remember { mutableStateOf(false) }
var avatarPressed by remember(message.id) { mutableStateOf(false) } var avatarPressed by remember(message.id) { mutableStateOf(false) }
var bubbleBodyPositionInRoot by remember { mutableStateOf(Offset.Zero) } var bubbleBodyPositionInRoot by remember { mutableStateOf(Offset.Zero) }
var slackRowLayoutCoords by remember(message.id) { mutableStateOf<LayoutCoordinates?>(null) }
val scaleTarget = if (isPressed && !isContextMenuForThisMessage && !isContextMenuOpen) 0.96f else 1f val scaleTarget = if (isPressed && !isContextMenuForThisMessage && !isContextMenuOpen) 0.96f else 1f
val avatarScaleTarget = if (avatarPressed && !isContextMenuOpen) 0.96f else 1f val avatarScaleTarget = if (avatarPressed && !isContextMenuOpen) 0.96f else 1f
val scale by animateFloatAsState( val scale by animateFloatAsState(
@@ -210,9 +212,9 @@ fun MessageItem(
bottomEnd = if (isAuthor) 8.dp else 20.dp bottomEnd = if (isAuthor) 8.dp else 20.dp
) )
val bubbleBodyGestures = val bubblePressAndLongPress =
if (isContextMenuOpen) Modifier if (isContextMenuOpen) Modifier
else Modifier.pointerInput(Unit) { else Modifier.pointerInput(isContextMenuOpen, message.id) {
detectTapGestures( detectTapGestures(
onPress = { onPress = {
isPressed = true isPressed = true
@@ -222,17 +224,49 @@ fun MessageItem(
isPressed = false isPressed = false
} }
}, },
onLongPress = { offset -> onLongPress = { localOffset ->
onTapPosition(bubbleBodyPositionInRoot + offset) onTapPosition(bubbleBodyPositionInRoot + localOffset)
onLongPress() onLongPress()
} }
) )
} }
val slackRowPressAndLongPress =
if (isContextMenuOpen) Modifier
else Modifier.pointerInput(isContextMenuOpen, message.id) {
detectTapGestures(
onPress = {
isPressed = true
try {
awaitRelease()
} finally {
isPressed = false
}
},
onLongPress = { localOffset ->
val coords = slackRowLayoutCoords
if (coords != null && coords.isAttached) {
onTapPosition(coords.localToRoot(localOffset))
} else {
onTapPosition(bubbleBodyPositionInRoot + localOffset)
}
onLongPress()
}
)
}
// Full-width hit target so pressing empty row space still scales the bubble & long-press menu.
Box(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { slackRowLayoutCoords = it }
.then(slackRowPressAndLongPress)
) {
// graphicsLayer must wrap clip/shadow/background so the whole bubble scales on press; // graphicsLayer must wrap clip/shadow/background so the whole bubble scales on press;
// placing it only after background scaled the children but left the bubble chrome unscaled. // placing it only after background scaled the children but left the bubble chrome unscaled.
Box( Box(
modifier = Modifier modifier = Modifier
.align(if (isAuthor) Alignment.BottomEnd else Alignment.BottomStart)
.widthIn(max = maxBubbleWidth) .widthIn(max = maxBubbleWidth)
.graphicsLayer( .graphicsLayer(
scaleX = scale, scaleX = scale,
@@ -310,7 +344,7 @@ fun MessageItem(
.onGloballyPositioned { coordinates -> .onGloballyPositioned { coordinates ->
bubbleBodyPositionInRoot = coordinates.positionInRoot() bubbleBodyPositionInRoot = coordinates.positionInRoot()
} }
.then(bubbleBodyGestures) .then(bubblePressAndLongPress)
) { ) {
Column { Column {
// Reply preview // Reply preview
@@ -534,6 +568,7 @@ fun MessageItem(
} }
} }
} }
}
} }
} }
} }