fix: correct re-centering animation making flicker
This commit is contained in:
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user