mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
Fix TV home row focus navigation
This commit is contained in:
@@ -24,6 +24,7 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusProperties
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -93,7 +94,11 @@ fun PosterCard(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(posterWidth)
|
||||
.graphicsLayer { scaleX = scale; scaleY = scale }
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
transformOrigin = TransformOrigin(0.5f, 0f)
|
||||
}
|
||||
) {
|
||||
Box() {
|
||||
PurefinAsyncImage(
|
||||
|
||||
@@ -33,15 +33,31 @@ fun TvHomeContent(
|
||||
val visibleLibraries = remember(libraries, libraryContent) {
|
||||
libraries.filter { libraryContent[it.id]?.isEmpty() != true }
|
||||
}
|
||||
val continueWatchingFocusRequester = remember { FocusRequester() }
|
||||
val nextUpFocusRequester = remember { FocusRequester() }
|
||||
val libraryFocusRequesters = remember(visibleLibraries.map { it.id }) {
|
||||
visibleLibraries.associate { it.id to FocusRequester() }
|
||||
val continueWatchingSectionFocusRequester = remember { FocusRequester() }
|
||||
val continueWatchingFirstItemFocusRequester = remember { FocusRequester() }
|
||||
val nextUpSectionFocusRequester = remember { FocusRequester() }
|
||||
val nextUpFirstItemFocusRequester = remember { FocusRequester() }
|
||||
val librarySectionFocusRequesters = remember(visibleLibraries.map { it.id }) {
|
||||
visibleLibraries.associate { library -> library.id to FocusRequester() }
|
||||
}
|
||||
val libraryFirstItemFocusRequesters = remember(visibleLibraries.map { it.id }) {
|
||||
visibleLibraries.associate { library -> library.id to FocusRequester() }
|
||||
}
|
||||
val firstSectionFocusRequester = when {
|
||||
continueWatching.isNotEmpty() -> continueWatchingFocusRequester
|
||||
nextUp.isNotEmpty() -> nextUpFocusRequester
|
||||
else -> visibleLibraries.firstOrNull()?.let { libraryFocusRequesters[it.id] }
|
||||
continueWatching.isNotEmpty() -> continueWatchingSectionFocusRequester
|
||||
nextUp.isNotEmpty() -> nextUpSectionFocusRequester
|
||||
else -> visibleLibraries.firstOrNull()?.let { librarySectionFocusRequesters[it.id] }
|
||||
}
|
||||
val firstVisibleLibrarySectionFocusRequester = visibleLibraries.firstOrNull()
|
||||
?.let { librarySectionFocusRequesters[it.id] }
|
||||
val firstHomeRowBelowContinueWatching = when {
|
||||
nextUp.isNotEmpty() -> nextUpSectionFocusRequester
|
||||
else -> firstVisibleLibrarySectionFocusRequester
|
||||
}
|
||||
val firstHomeRowAboveLibraries = when {
|
||||
nextUp.isNotEmpty() -> nextUpSectionFocusRequester
|
||||
continueWatching.isNotEmpty() -> continueWatchingSectionFocusRequester
|
||||
else -> null
|
||||
}
|
||||
|
||||
LaunchedEffect(firstSectionFocusRequester) {
|
||||
@@ -59,11 +75,9 @@ fun TvHomeContent(
|
||||
item {
|
||||
TvContinueWatchingSection(
|
||||
items = continueWatching,
|
||||
firstItemFocusRequester = continueWatchingFocusRequester,
|
||||
downFocusRequester = when {
|
||||
nextUp.isNotEmpty() -> nextUpFocusRequester
|
||||
else -> visibleLibraries.firstOrNull()?.let { libraryFocusRequesters[it.id] }
|
||||
},
|
||||
sectionFocusRequester = continueWatchingSectionFocusRequester,
|
||||
firstItemFocusRequester = continueWatchingFirstItemFocusRequester,
|
||||
downFocusRequester = firstHomeRowBelowContinueWatching,
|
||||
onMovieSelected = onMovieSelected,
|
||||
onEpisodeSelected = onEpisodeSelected
|
||||
)
|
||||
@@ -74,9 +88,11 @@ fun TvHomeContent(
|
||||
item {
|
||||
TvNextUpSection(
|
||||
items = nextUp,
|
||||
firstItemFocusRequester = nextUpFocusRequester,
|
||||
upFocusRequester = continueWatching.takeIf { it.isNotEmpty() }?.let { continueWatchingFocusRequester },
|
||||
downFocusRequester = visibleLibraries.firstOrNull()?.let { libraryFocusRequesters[it.id] },
|
||||
sectionFocusRequester = nextUpSectionFocusRequester,
|
||||
firstItemFocusRequester = nextUpFirstItemFocusRequester,
|
||||
upFocusRequester = continueWatching.takeIf { it.isNotEmpty() }
|
||||
?.let { continueWatchingSectionFocusRequester },
|
||||
downFocusRequester = firstVisibleLibrarySectionFocusRequester,
|
||||
onEpisodeSelected = onEpisodeSelected
|
||||
)
|
||||
}
|
||||
@@ -88,28 +104,25 @@ fun TvHomeContent(
|
||||
key = { it.id }
|
||||
) { item ->
|
||||
val libraryIndex = visibleLibraries.indexOfFirst { it.id == item.id }
|
||||
val previousLibraryFocusRequester = visibleLibraries
|
||||
val previousLibrarySectionFocusRequester = visibleLibraries
|
||||
.getOrNull(libraryIndex - 1)
|
||||
?.let { libraryFocusRequesters[it.id] }
|
||||
val nextLibraryFocusRequester = visibleLibraries
|
||||
?.let { librarySectionFocusRequesters[it.id] }
|
||||
val nextLibrarySectionFocusRequester = visibleLibraries
|
||||
.getOrNull(libraryIndex + 1)
|
||||
?.let { libraryFocusRequesters[it.id] }
|
||||
?.let { librarySectionFocusRequesters[it.id] }
|
||||
|
||||
TvLibraryPosterSection(
|
||||
title = item.name,
|
||||
items = libraryContent[item.id] ?: emptyList(),
|
||||
action = "See All",
|
||||
firstItemFocusRequester = libraryFocusRequesters[item.id],
|
||||
sectionFocusRequester = librarySectionFocusRequesters.getValue(item.id),
|
||||
firstItemFocusRequester = libraryFirstItemFocusRequesters.getValue(item.id),
|
||||
upFocusRequester = if (libraryIndex == 0) {
|
||||
when {
|
||||
nextUp.isNotEmpty() -> nextUpFocusRequester
|
||||
continueWatching.isNotEmpty() -> continueWatchingFocusRequester
|
||||
else -> null
|
||||
}
|
||||
firstHomeRowAboveLibraries
|
||||
} else {
|
||||
previousLibraryFocusRequester
|
||||
previousLibrarySectionFocusRequester
|
||||
},
|
||||
downFocusRequester = nextLibraryFocusRequester,
|
||||
downFocusRequester = nextLibrarySectionFocusRequester,
|
||||
onMovieSelected = onMovieSelected,
|
||||
onSeriesSelected = onSeriesSelected,
|
||||
onEpisodeSelected = onEpisodeSelected
|
||||
|
||||
@@ -32,6 +32,8 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusProperties
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -54,6 +56,7 @@ import kotlin.math.nextUp
|
||||
@Composable
|
||||
fun TvContinueWatchingSection(
|
||||
items: List<ContinueWatchingItem>,
|
||||
sectionFocusRequester: FocusRequester,
|
||||
firstItemFocusRequester: FocusRequester? = null,
|
||||
upFocusRequester: FocusRequester? = null,
|
||||
downFocusRequester: FocusRequester? = null,
|
||||
@@ -67,7 +70,10 @@ fun TvContinueWatchingSection(
|
||||
action = null
|
||||
)
|
||||
LazyRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(sectionFocusRequester)
|
||||
.focusRestorer(firstItemFocusRequester ?: FocusRequester.Default),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
@@ -161,7 +167,11 @@ fun TvContinueWatchingCard(
|
||||
modifier = modifier
|
||||
.width(cardWidth)
|
||||
.wrapContentHeight()
|
||||
.graphicsLayer { scaleX = scale; scaleY = scale }
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
transformOrigin = TransformOrigin(0.5f, 0f)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -217,6 +227,7 @@ fun TvContinueWatchingCard(
|
||||
@Composable
|
||||
fun TvNextUpSection(
|
||||
items: List<NextUpItem>,
|
||||
sectionFocusRequester: FocusRequester,
|
||||
firstItemFocusRequester: FocusRequester? = null,
|
||||
upFocusRequester: FocusRequester? = null,
|
||||
downFocusRequester: FocusRequester? = null,
|
||||
@@ -229,7 +240,10 @@ fun TvNextUpSection(
|
||||
action = null
|
||||
)
|
||||
LazyRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(sectionFocusRequester)
|
||||
.focusRestorer(firstItemFocusRequester ?: FocusRequester.Default),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
@@ -313,7 +327,11 @@ fun TvNextUpCard(
|
||||
modifier = modifier
|
||||
.width(cardWidth)
|
||||
.wrapContentHeight()
|
||||
.graphicsLayer { scaleX = scale; scaleY = scale }
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
transformOrigin = TransformOrigin(0.5f, 0f)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -364,6 +382,7 @@ fun TvLibraryPosterSection(
|
||||
title: String,
|
||||
items: List<PosterItem>,
|
||||
action: String?,
|
||||
sectionFocusRequester: FocusRequester,
|
||||
firstItemFocusRequester: FocusRequester? = null,
|
||||
upFocusRequester: FocusRequester? = null,
|
||||
downFocusRequester: FocusRequester? = null,
|
||||
@@ -377,7 +396,10 @@ fun TvLibraryPosterSection(
|
||||
action = action
|
||||
)
|
||||
LazyRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(sectionFocusRequester)
|
||||
.focusRestorer(firstItemFocusRequester ?: FocusRequester.Default),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user