diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt index 98dee03..819fbd8 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt @@ -18,8 +18,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.snapshotFlow +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -27,6 +28,7 @@ import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import fr.ajaury.gwenedeg.core.model.Phrase import fr.ajaury.gwenedeg.preferences.model.SubtitlePreferences import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine @@ -44,19 +46,24 @@ fun SubtitleList( ) { val listState = rememberLazyListState() val isDragged by listState.interactionSource.collectIsDraggedAsState() + var targetIndex by remember { mutableStateOf(0) } + var isDragging by remember { mutableStateOf(false) } - val centeredIndex by remember { + // Detect beginning of dragging. + LaunchedEffect(isDragged) { + if (isDragged) { + isDragging = true + } + } + + val centeredIndex by remember(currentIndex, isDragged) { derivedStateOf { - val info = listState.layoutInfo - if (info.visibleItemsInfo.isEmpty()) { - -1 + if (isDragging) { + listState.getCenteredIndex().also { + targetIndex = it + } } else { - val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2f - info.visibleItemsInfo - .minByOrNull { - abs((it.offset + it.size / 2f) - viewportCenter) - }?.index - ?: -1 + currentIndex ?: -1 } } } @@ -69,21 +76,15 @@ fun SubtitleList( } // Snap to the centered line and seek to it once the user finishes scrolling. - LaunchedEffect(listState, lines) { - var wasDragged = false - snapshotFlow { isDragged to listState.isScrollInProgress } - .collect { (dragged, scrolling) -> - if (dragged) { - wasDragged = true - } else if (wasDragged && !scrolling) { - wasDragged = false - val targetIndex = centeredIndex - if (targetIndex in lines.indices) { - listState.centerItem(targetIndex) - onSeek(targetIndex) - } - } + LaunchedEffect(isDragged) { + if (isDragging && !isDragged) { + if (targetIndex in lines.indices) { + listState.centerItem(targetIndex) + onSeek(targetIndex) } + + isDragging = false + } } BoxWithConstraints(modifier = modifier) { @@ -107,6 +108,20 @@ fun SubtitleList( } } +private fun LazyListState.getCenteredIndex(): Int { + val info = this.layoutInfo + return if (info.visibleItemsInfo.isEmpty()) { + -1 + } else { + val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2f + info.visibleItemsInfo + .minByOrNull { + abs((it.offset + it.size / 2f) - viewportCenter) + }?.index + ?: -1 + } +} + private suspend fun LazyListState.centerItem(index: Int) { if (index < 0 || index >= this.layoutInfo.totalItemsCount) return @@ -126,11 +141,8 @@ private fun Subtitle( isCurrent: Boolean, subtitlePreferences: SubtitlePreferences = SubtitlePreferences(), ) { - val contentColor = if (isCurrent) { - Color.Unspecified - } else { - MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f) - } + val textAlpha = if (isCurrent) 1f else 0.35f + val contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = textAlpha) Column( modifier = Modifier.fillMaxWidth(), @@ -140,7 +152,6 @@ private fun Subtitle( if (subtitlePreferences.showTranscription) { TranscriptionLine( text = line.phrase.transcription, - isCurrent = isCurrent, contentColor = contentColor, ) } @@ -149,7 +160,6 @@ private fun Subtitle( ?.let { translation -> TranslationLine( text = translation, - isCurrent = isCurrent, contentColor = contentColor, ) } @@ -159,18 +169,11 @@ private fun Subtitle( @Composable private fun TranscriptionLine( text: String, - isCurrent: Boolean, contentColor: Color, ) { - val mainTextStyle = if (isCurrent) { - MaterialTheme.typography.headlineSmall - } else { - MaterialTheme.typography.bodyLarge - } - Text( text = text, - style = mainTextStyle, + style = MaterialTheme.typography.bodyLarge.copy(fontSize = 20.sp), color = contentColor, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth(), @@ -180,18 +183,11 @@ private fun TranscriptionLine( @Composable private fun TranslationLine( text: String, - isCurrent: Boolean, contentColor: Color, ) { - val translationStyle = if (isCurrent) { - MaterialTheme.typography.bodyLarge - } else { - MaterialTheme.typography.bodySmall - } - Text( text = text, - style = translationStyle.copy(fontStyle = FontStyle.Italic), + style = MaterialTheme.typography.bodySmall.copy(fontStyle = FontStyle.Italic), color = contentColor, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth(),