fix(live): left lands on the selected group, right enters a fresh group at the top
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,15 +14,21 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
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.focusProperties
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -76,6 +82,18 @@ fun LiveScreen(state: AppState) {
|
||||
return
|
||||
}
|
||||
|
||||
// D-pad choreography: left from any channel row lands on the SELECTED
|
||||
// group (not the spatially nearest one), right from the rail enters the
|
||||
// list at the top — which also gets scrolled up on every group change.
|
||||
val railFocus = remember { FocusRequester() }
|
||||
val listFocus = remember { FocusRequester() }
|
||||
val listState = rememberLazyListState()
|
||||
LaunchedEffect(state.groupFilter, state.favoritesOnly) {
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
val intoList = if (channels.isEmpty()) Modifier
|
||||
else Modifier.focusProperties { right = listFocus }
|
||||
|
||||
Row(Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
@@ -87,10 +105,14 @@ fun LiveScreen(state: AppState) {
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
item {
|
||||
val selected = !state.favoritesOnly && state.groupFilter == null
|
||||
GroupItem(
|
||||
label = "Alle Sender",
|
||||
count = allChannels.size,
|
||||
selected = !state.favoritesOnly && state.groupFilter == null,
|
||||
selected = selected,
|
||||
modifier = intoList.then(
|
||||
if (selected) Modifier.focusRequester(railFocus) else Modifier
|
||||
),
|
||||
) {
|
||||
state.favoritesOnly = false
|
||||
state.groupFilter = null
|
||||
@@ -102,6 +124,9 @@ fun LiveScreen(state: AppState) {
|
||||
label = "★ Favoriten",
|
||||
count = favorites.size,
|
||||
selected = state.favoritesOnly,
|
||||
modifier = intoList.then(
|
||||
if (state.favoritesOnly) Modifier.focusRequester(railFocus) else Modifier
|
||||
),
|
||||
) {
|
||||
state.favoritesOnly = true
|
||||
state.groupFilter = null
|
||||
@@ -122,6 +147,9 @@ fun LiveScreen(state: AppState) {
|
||||
label = group,
|
||||
count = remember(allChannels, group) { allChannels.count { it.group == group } },
|
||||
selected = state.groupFilter == group,
|
||||
modifier = intoList.then(
|
||||
if (state.groupFilter == group) Modifier.focusRequester(railFocus) else Modifier
|
||||
),
|
||||
) {
|
||||
state.favoritesOnly = false
|
||||
state.groupFilter = group
|
||||
@@ -130,6 +158,7 @@ fun LiveScreen(state: AppState) {
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight(),
|
||||
@@ -151,12 +180,15 @@ fun LiveScreen(state: AppState) {
|
||||
)
|
||||
}
|
||||
}
|
||||
items(channels, key = { it.url + it.name }) { channel ->
|
||||
itemsIndexed(channels, key = { _, c -> c.url + c.name }) { listIndex, channel ->
|
||||
ChannelRow(
|
||||
channel = channel,
|
||||
// Stable channel number = position in "Alle Sender",
|
||||
// matching the remote's number pad in every view.
|
||||
number = allChannels.indexOf(channel) + 1,
|
||||
modifier = Modifier
|
||||
.focusProperties { left = railFocus }
|
||||
.then(if (listIndex == 0) Modifier.focusRequester(listFocus) else Modifier),
|
||||
showGroup = state.groupFilter == null,
|
||||
nowNext = state.nowNext(channel),
|
||||
playing = state.currentChannel?.url == channel.url,
|
||||
@@ -175,12 +207,18 @@ fun LiveScreen(state: AppState) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GroupItem(label: String, count: Int, selected: Boolean, onSelect: () -> Unit) {
|
||||
private fun GroupItem(
|
||||
label: String,
|
||||
count: Int,
|
||||
selected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onSelect: () -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
// Focusing a group opens it right away (browse like a menu);
|
||||
// clicking is not required, but harmless.
|
||||
onClick = onSelect,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.onFocusChanged { if (it.isFocused) onSelect() },
|
||||
shape = ClickableSurfaceDefaults.shape(RoundedCornerShape(10.dp)),
|
||||
@@ -218,6 +256,7 @@ private fun GroupItem(label: String, count: Int, selected: Boolean, onSelect: ()
|
||||
private fun ChannelRow(
|
||||
channel: Channel,
|
||||
number: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
showGroup: Boolean,
|
||||
nowNext: NowNext,
|
||||
playing: Boolean,
|
||||
@@ -229,7 +268,7 @@ private fun ChannelRow(
|
||||
Surface(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
shape = ClickableSurfaceDefaults.shape(RoundedCornerShape(12.dp)),
|
||||
colors = ClickableSurfaceDefaults.colors(
|
||||
containerColor = if (playing) CastarrColors.accentDim else Color.Transparent,
|
||||
|
||||
Reference in New Issue
Block a user