-
Notifications
You must be signed in to change notification settings - Fork 309
Expand file tree
/
Copy pathTvPodcastGridScaffold.kt
More file actions
93 lines (88 loc) 路 3.75 KB
/
Copy pathTvPodcastGridScaffold.kt
File metadata and controls
93 lines (88 loc) 路 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package au.com.shiftyjelly.pocketcasts.component
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
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.Color
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Text
import au.com.shiftyjelly.pocketcasts.theme.TvTextStyles
import kotlinx.coroutines.flow.first
@Composable
internal fun TvPodcastGridScaffold(
title: String,
itemKeys: List<Any>,
modifier: Modifier = Modifier,
autoFocusFirstItem: Boolean = false,
itemContent: @Composable (index: Int, itemModifier: Modifier) -> Unit,
) {
Column(modifier = modifier) {
Text(
text = title,
style = TvTextStyles.ScreenTitle,
color = Color.White,
modifier = Modifier.padding(start = 32.dp, top = 8.dp, bottom = 10.dp),
)
val gridState = rememberLazyGridState()
var lastFocusedKey by rememberSaveable { mutableStateOf<String?>(null) }
val focusRequesters = remember(itemKeys.size) { List(itemKeys.size) { FocusRequester() } }
if (autoFocusFirstItem) {
LaunchedEffect(Unit) {
snapshotFlow { gridState.layoutInfo.visibleItemsInfo.isNotEmpty() }.first { it }
runCatching { focusRequesters.firstOrNull()?.requestFocus() }
}
}
LazyVerticalGrid(
state = gridState,
columns = GridCells.Fixed(GRID_COLUMNS),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
modifier = Modifier
.focusGroup()
.focusProperties {
onEnter = {
val visible = gridState.layoutInfo.visibleItemsInfo
val target = itemKeys.indexOfFirst { it.toString() == lastFocusedKey }
.takeIf { index -> index >= 0 && visible.any { it.index == index } }
?: visible.firstOrNull()?.index
target?.let { runCatching { focusRequesters.getOrNull(it)?.requestFocus() } }
}
},
) {
items(
count = itemKeys.size,
key = { index -> itemKeys[index] },
) { index ->
itemContent(
index,
Modifier
.focusRequester(focusRequesters[index])
.onFocusChanged { focusState ->
if (focusState.hasFocus) {
lastFocusedKey = itemKeys[index].toString()
}
},
)
}
}
}
}
private const val GRID_COLUMNS = 6