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