Browse Source

voice: fix waveform — pointerInput on Box, cursor + white bars stay on pause

- Canvas pointerInput moved to wrapping Box (state updates trigger recomposition)
- showCursor = playing || paused (cursor and white bars visible on pause)
- elapsed/dur stays visible on pause too
topo_upd
evgeny 2 months ago
parent
commit
2e8aa89397
  1. 61
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt

61
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt

@ -129,36 +129,39 @@ private fun VoiceBubble(message: Message, textColor: Color,
/* Waveform with seek on tap */ /* Waveform with seek on tap */
if (wfCount >= 2) { if (wfCount >= 2) {
Canvas(modifier = Modifier.weight(1f).height(28.dp) Box(
.pointerInput(message.filePath) { modifier = Modifier.weight(1f).height(28.dp)
detectTapGestures { offset -> .pointerInput(message.filePath) {
if (message.filePath.isEmpty()) return@detectTapGestures detectTapGestures { offset ->
val seekFrac = (offset.x / size.width).coerceIn(0f, 1f) if (message.filePath.isEmpty()) return@detectTapGestures
val seekMs = (seekFrac * durMs).toInt() val seekFrac = (offset.x / size.width).coerceIn(0f, 1f)
playing = true; paused = false; elapsedMs = seekMs; progress = seekFrac val seekMs = (seekFrac * durMs).toInt()
onSeek(message.filePath, seekMs, onDone) playing = true; paused = false; elapsedMs = seekMs; progress = seekFrac
onSeek(message.filePath, seekMs, onDone)
}
} }
}
) { ) {
val barW = size.width / wfCount Canvas(modifier = Modifier.fillMaxSize()) {
val ph = size.height val barW = size.width / wfCount
val showCursor = playing && !paused val ph = size.height
val playedIdx = if (showCursor && progress > 0f) (progress * wfCount).toInt() else 0 val showCursor = playing || paused
for (i in 0 until wfCount) { val playedIdx = if (showCursor && progress > 0f) (progress * wfCount).toInt() else 0
val level = wf[i].coerceIn(0f, 1f) for (i in 0 until wfCount) {
val barH = (2f + level * (ph - 2f)).coerceAtMost(ph) val level = wf[i].coerceIn(0f, 1f)
val x = i.toFloat() * barW val barH = (2f + level * (ph - 2f)).coerceAtMost(ph)
val y = ph - barH val x = i.toFloat() * barW
val played = showCursor && i < playedIdx val y = ph - barH
drawRect( val played = showCursor && i < playedIdx
color = when { played -> Color.White; else -> Color(0xFFCCCCD4).copy(alpha = 0.8f) }, drawRect(
topLeft = Offset(x, y), color = when { played -> Color.White; else -> Color(0xFFCCCCD4).copy(alpha = 0.8f) },
size = Size(barW * 0.75f, barH) topLeft = Offset(x, y),
) size = Size(barW * 0.75f, barH)
} )
if (showCursor && progress > 0f && progress < 1f) { }
val cursorX = progress * size.width if (showCursor && progress > 0f && progress < 1f) {
drawLine(Color(0xFFFFD600), Offset(cursorX, 0f), Offset(cursorX, ph), strokeWidth = 2.5f) val cursorX = progress * size.width
drawLine(Color(0xFFFFD600), Offset(cursorX, 0f), Offset(cursorX, ph), strokeWidth = 2.5f)
}
} }
} }
} }
@ -173,7 +176,7 @@ private fun VoiceBubble(message: Message, textColor: Color,
verticalAlignment = Alignment.Bottom verticalAlignment = Alignment.Bottom
) { ) {
Text( Text(
if (playing && !paused) "${formatDurationMs(elapsedMs)} / ${formatDurationMs(durMs)}" if (playing || paused) "${formatDurationMs(elapsedMs)} / ${formatDurationMs(durMs)}"
else formatDurationMs(durMs), else formatDurationMs(durMs),
fontSize = 12.sp, fontSize = 12.sp,
color = textColor.copy(alpha = 0.55f) color = textColor.copy(alpha = 0.55f)

Loading…
Cancel
Save