mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-08-07 17:19:53 +00:00
Add window width class and TV focus restoration
Introduce a runtime WindowWidthClass (Compact/Medium/Expanded) provided at the app root and use it to drive responsive grid columns on Discover and Library. Add initial focus, focusRestorer, and focusGroup to the TV home and sidebar, apply focusRestorer to the shared Discover/Library grids, and strengthen the focused card highlight.
This commit is contained in:
parent
0860871297
commit
e558bb84af
7 changed files with 76 additions and 10 deletions
|
|
@ -1,6 +1,9 @@
|
|||
package com.fluxa.app.shared
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -356,6 +359,13 @@ fun FluxaAppHost(
|
|||
}
|
||||
}
|
||||
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
||||
val widthClass = if (deviceType == com.fluxa.app.ui.catalog.DeviceType.TV) {
|
||||
com.fluxa.app.ui.catalog.WindowWidthClass.Expanded
|
||||
} else {
|
||||
com.fluxa.app.ui.catalog.widthClassFor(maxWidth)
|
||||
}
|
||||
CompositionLocalProvider(com.fluxa.app.ui.catalog.LocalWindowWidthClass provides widthClass) {
|
||||
FluxaApp(
|
||||
state = appState.uiState,
|
||||
deviceType = deviceType,
|
||||
|
|
@ -557,4 +567,6 @@ fun FluxaAppHost(
|
|||
showNavigationBar = showNavigationBar,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
|
||||
package com.fluxa.app.shared
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -26,6 +29,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
|
@ -59,7 +63,9 @@ fun TvSidebarNav(
|
|||
.fillMaxHeight()
|
||||
.width(84.dp)
|
||||
.background(Color.Black.copy(alpha = 0.55f))
|
||||
.padding(vertical = 24.dp),
|
||||
.padding(vertical = 24.dp)
|
||||
.focusGroup()
|
||||
.focusRestorer(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically)
|
||||
) {
|
||||
TvSidebarItems.forEach { item ->
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
|
||||
package com.fluxa.app.shared.feature.catalog
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -18,10 +20,16 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||
import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -38,17 +46,25 @@ fun TvCatalogHomeScreen(
|
|||
language: String? = null,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val columnFocus = remember { FocusRequester() }
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
if (state.rows.isEmpty()) {
|
||||
TvCatalogHomeLoading(Modifier.fillMaxSize())
|
||||
} else {
|
||||
LaunchedEffect(Unit) { runCatching { columnFocus.requestFocus() } }
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.focusRequester(columnFocus)
|
||||
.focusRestorer(),
|
||||
contentPadding = PaddingValues(top = 44.dp, bottom = 64.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(30.dp)
|
||||
) {
|
||||
items(state.rows, key = { it.id }) { row ->
|
||||
Column(verticalArrangement = Arrangement.spacedBy(14.dp)) {
|
||||
Column(
|
||||
modifier = Modifier.focusGroup(),
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
Text(
|
||||
text = row.title,
|
||||
color = Color.White,
|
||||
|
|
@ -59,6 +75,7 @@ fun TvCatalogHomeScreen(
|
|||
modifier = Modifier.padding(horizontal = 58.dp)
|
||||
)
|
||||
LazyRow(
|
||||
modifier = Modifier.focusRestorer(),
|
||||
contentPadding = PaddingValues(horizontal = 58.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(20.dp)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
|
||||
package com.fluxa.app.shared.feature.discover
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -59,6 +62,8 @@ import com.fluxa.app.shared.feature.search.SearchResultRows
|
|||
import com.fluxa.app.shared.feature.search.SearchResults
|
||||
import com.fluxa.app.ui.catalog.CatalogCard
|
||||
import com.fluxa.app.ui.catalog.FluxaColors
|
||||
import com.fluxa.app.ui.catalog.LocalWindowWidthClass
|
||||
import com.fluxa.app.ui.catalog.gridColumns
|
||||
|
||||
@Composable
|
||||
fun DiscoverScreen(
|
||||
|
|
@ -141,8 +146,8 @@ fun DiscoverScreen(
|
|||
}
|
||||
LazyVerticalGrid(
|
||||
state = gridState,
|
||||
columns = GridCells.Fixed(3),
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
columns = GridCells.Fixed(LocalWindowWidthClass.current.gridColumns()),
|
||||
modifier = Modifier.weight(1f).fillMaxWidth().focusRestorer(),
|
||||
contentPadding = PaddingValues(bottom = 120.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
|
|
@ -201,7 +206,7 @@ private fun DiscoverSearchField(
|
|||
@Composable
|
||||
private fun DiscoverSkeletonGrid(modifier: Modifier = Modifier) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
columns = GridCells.Fixed(LocalWindowWidthClass.current.gridColumns()),
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(bottom = 120.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
|
||||
package com.fluxa.app.shared.feature.library
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -43,6 +45,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -55,6 +58,8 @@ import com.fluxa.app.shared.feature.discover.DiscoverFilterOptionUiModel
|
|||
import com.fluxa.app.shared.image.FluxaRemoteImage
|
||||
import com.fluxa.app.ui.catalog.CatalogCard
|
||||
import com.fluxa.app.ui.catalog.FluxaColors
|
||||
import com.fluxa.app.ui.catalog.LocalWindowWidthClass
|
||||
import com.fluxa.app.ui.catalog.gridColumns
|
||||
|
||||
@Composable
|
||||
fun LibraryScreen(
|
||||
|
|
@ -265,11 +270,11 @@ private fun LibraryItemGrid(
|
|||
return
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
columns = GridCells.Fixed(LocalWindowWidthClass.current.gridColumns()),
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 120.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier.fillMaxSize().focusRestorer()
|
||||
) {
|
||||
items(items, key = { "${it.type}:${it.id}" }) { item ->
|
||||
CatalogCard(model = item.card.copy(showTitleBar = true), onClick = { onItemSelected(item) })
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ fun CatalogCard(
|
|||
.then(
|
||||
if (focused) {
|
||||
Modifier
|
||||
.scale(1.05f)
|
||||
.border(2.dp, Color.White, RoundedCornerShape(8.dp))
|
||||
.scale(1.1f)
|
||||
.border(3.dp, Color.White, RoundedCornerShape(8.dp))
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.fluxa.app.ui.catalog
|
||||
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
enum class WindowWidthClass { Compact, Medium, Expanded }
|
||||
|
||||
val LocalWindowWidthClass = compositionLocalOf { WindowWidthClass.Compact }
|
||||
|
||||
fun widthClassFor(maxWidth: Dp): WindowWidthClass = when {
|
||||
maxWidth < 600.dp -> WindowWidthClass.Compact
|
||||
maxWidth < 840.dp -> WindowWidthClass.Medium
|
||||
else -> WindowWidthClass.Expanded
|
||||
}
|
||||
|
||||
fun WindowWidthClass.gridColumns(): Int = when (this) {
|
||||
WindowWidthClass.Compact -> 3
|
||||
WindowWidthClass.Medium -> 5
|
||||
WindowWidthClass.Expanded -> 7
|
||||
}
|
||||
Loading…
Reference in a new issue