made language selection model scrollable

This commit is contained in:
tapframe 2026-02-26 05:22:46 +05:30
parent 04d7c5a3f7
commit 0e805be37e
3 changed files with 69 additions and 26 deletions

View file

@ -93,6 +93,7 @@ fun HeroContentSection(
isTrailerPlaying: Boolean = false,
playButtonFocusRequester: FocusRequester? = null,
restorePlayFocusToken: Int = 0,
onHeroActionFocused: () -> Unit = {},
onPlayFocusRestored: () -> Unit = {}
) {
val context = LocalContext.current
@ -221,7 +222,10 @@ fun HeroContentSection(
onClick = onPlayClick,
focusRequester = playButtonFocusRequester,
restoreFocusToken = restorePlayFocusToken,
onFocusRestored = onPlayFocusRestored
onFocusRestored = {
onHeroActionFocused()
onPlayFocusRestored()
}
)
ActionIconButton(
@ -233,7 +237,8 @@ fun HeroContentSection(
},
contentDescription = if (isInLibrary) stringResource(R.string.hero_remove_from_library) else stringResource(R.string.hero_add_to_library),
onClick = onToggleLibrary,
onLongPress = onLibraryLongPress
onLongPress = onLibraryLongPress,
onFocused = onHeroActionFocused
)
if (meta.apiType == "movie") {
@ -252,7 +257,8 @@ fun HeroContentSection(
enabled = !isMovieWatchedPending,
selected = isMovieWatched,
selectedContainerColor = Color.White,
selectedContentColor = Color.Black
selectedContentColor = Color.Black,
onFocused = onHeroActionFocused
)
}
@ -260,7 +266,8 @@ fun HeroContentSection(
ActionIconButtonPainter(
painter = trailerPainter,
contentDescription = stringResource(R.string.hero_play_trailer),
onClick = onTrailerClick
onClick = onTrailerClick,
onFocused = onHeroActionFocused
)
}
}
@ -375,6 +382,7 @@ private fun ActionIconButtonPainter(
painter: Painter,
contentDescription: String,
onClick: () -> Unit,
onFocused: () -> Unit = {},
enabled: Boolean = true
) {
IconButton(
@ -382,6 +390,9 @@ private fun ActionIconButtonPainter(
enabled = enabled,
modifier = Modifier
.size(48.dp)
.onFocusChanged { state ->
if (state.isFocused) onFocused()
}
.focusProperties { up = FocusRequester.Cancel },
colors = IconButtonDefaults.colors(
containerColor = NuvioColors.BackgroundCard,
@ -418,7 +429,8 @@ private fun ActionIconButton(
enabled: Boolean = true,
selected: Boolean = false,
selectedContainerColor: Color = Color(0xFF7CFF9B),
selectedContentColor: Color = Color.Black
selectedContentColor: Color = Color.Black,
onFocused: () -> Unit = {}
) {
var longPressTriggered by remember { mutableStateOf(false) }
@ -433,6 +445,9 @@ private fun ActionIconButton(
enabled = enabled,
modifier = Modifier
.size(48.dp)
.onFocusChanged { state ->
if (state.isFocused) onFocused()
}
.onPreviewKeyEvent { event ->
val native = event.nativeKeyEvent
if (onLongPress != null && native.action == AndroidKeyEvent.ACTION_DOWN) {

View file

@ -96,7 +96,9 @@ import com.nuvio.tv.ui.components.NuvioDialog
import com.nuvio.tv.ui.components.TrailerPlayer
import com.nuvio.tv.ui.theme.NuvioColors
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import androidx.compose.ui.window.Dialog
import androidx.compose.runtime.rememberCoroutineScope
private enum class RestoreTarget {
HERO,
@ -599,6 +601,7 @@ private fun MetaDetailsContent(
mutableStateOf(false)
}
val lifecycleOwner = LocalLifecycleOwner.current
val coroutineScope = rememberCoroutineScope()
fun clearPendingRestore() {
pendingRestoreType = null
@ -1017,6 +1020,15 @@ private fun MetaDetailsContent(
hideLogoDuringTrailer = hideLogoDuringTrailer,
isTrailerPlaying = isTrailerPlaying,
playButtonFocusRequester = heroPlayFocusRequester,
onHeroActionFocused = {
if (listState.firstVisibleItemIndex > 0 || listState.firstVisibleItemScrollOffset > 0) {
coroutineScope.launch {
listState.animateScrollToItem(0)
}
}
initialHeroFocusRequested = true
clearPendingRestore()
},
restorePlayFocusToken = (if (pendingRestoreType == RestoreTarget.HERO) restoreFocusToken else 0) +
restorePlayFocusAfterTrailerBackToken,
onPlayFocusRestored = {

View file

@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
@ -179,28 +180,43 @@ fun ThemeSettingsContent(
width = 400.dp,
suppressFirstKeyUp = false
) {
supportedLocales.forEachIndexed { index, (tag, name) ->
val isSelected = tag == selectedTag
Button(
onClick = {
val previousTag = selectedTag
context.getSharedPreferences("app_locale", android.content.Context.MODE_PRIVATE)
.edit().putString("locale_tag", tag ?: "").apply()
selectedTag = tag
showLanguageDialog = false
if (previousTag != tag) {
pendingLanguageRestart = true
}
},
modifier = Modifier
.fillMaxWidth()
.then(if (index == 0) Modifier.focusRequester(firstFocusRequester) else Modifier),
colors = ButtonDefaults.colors(
containerColor = if (isSelected) NuvioColors.FocusBackground else NuvioColors.BackgroundCard,
contentColor = NuvioColors.TextPrimary
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(280.dp)
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(vertical = 2.dp)
) {
Text(name)
for (index in supportedLocales.indices) {
val (tag, name) = supportedLocales[index]
item {
val isSelected = tag == selectedTag
Button(
onClick = {
val previousTag = selectedTag
context.getSharedPreferences("app_locale", android.content.Context.MODE_PRIVATE)
.edit().putString("locale_tag", tag ?: "").apply()
selectedTag = tag
showLanguageDialog = false
if (previousTag != tag) {
pendingLanguageRestart = true
}
},
modifier = Modifier
.fillMaxWidth()
.then(if (index == 0) Modifier.focusRequester(firstFocusRequester) else Modifier),
colors = ButtonDefaults.colors(
containerColor = if (isSelected) NuvioColors.FocusBackground else NuvioColors.BackgroundCard,
contentColor = NuvioColors.TextPrimary
)
) {
Text(name)
}
}
}
}
}
}