Add dimming effect to highlight which episode is currenty selected (#232)

On the series overview page, when moving off of the episode row, such as
to do the play/more buttons, the other episodes will now become dimmer
to indicate which episode you're interacting with.

Also, when moving back to the page from the nav drawer, focus is
restored to where it left from. This is useful when scrolling fast left
on the season tabs and you overshoot onto the nav drawer, moving right
once will always move back to the season tabs.

In this example screenshot, Episode 3 is selected, so the others are
darker.
<img width="1280" height="771" alt="dim Large"
src="https://github.com/user-attachments/assets/fa21ade4-3345-4e97-873f-d654123a1ae5"
/>

This is a follow up from #158
This commit is contained in:
damontecres 2025-11-16 21:23:55 -05:00 committed by GitHub
parent e3d94ad819
commit b42aa297f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,7 @@
package com.github.damontecres.wholphin.ui.detail.series package com.github.damontecres.wholphin.ui.detail.series
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -15,11 +17,13 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.key import androidx.compose.runtime.key
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@ -30,6 +34,7 @@ import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusRestorer import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
@ -86,6 +91,9 @@ fun SeriesOverviewContent(
val focusedEpisode = val focusedEpisode =
(episodes as? EpisodeList.Success)?.episodes?.getOrNull(position.episodeRowIndex) (episodes as? EpisodeList.Success)?.episodes?.getOrNull(position.episodeRowIndex)
var pageHasFocus by remember { mutableStateOf(false) }
var cardRowHasFocus by remember { mutableStateOf(false) }
val dimming by animateFloatAsState(if (pageHasFocus && !cardRowHasFocus) .4f else 1f)
Box( Box(
modifier = modifier =
@ -127,7 +135,10 @@ fun SeriesOverviewContent(
LazyColumn( LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
modifier = Modifier, modifier =
Modifier
.focusRestorer(episodeRowFocusRequester)
.onFocusChanged { pageHasFocus = it.hasFocus },
) { ) {
item { item {
val paddingValues = val paddingValues =
@ -150,6 +161,7 @@ fun SeriesOverviewContent(
modifier = modifier =
Modifier Modifier
.focusRequester(tabRowFocusRequester) .focusRequester(tabRowFocusRequester)
.focusRestorer()
.padding(paddingValues) .padding(paddingValues)
.fillMaxWidth(), .fillMaxWidth(),
) )
@ -193,7 +205,10 @@ fun SeriesOverviewContent(
modifier = modifier =
Modifier Modifier
.focusRestorer(firstItemFocusRequester) .focusRestorer(firstItemFocusRequester)
.focusRequester(episodeRowFocusRequester), .focusRequester(episodeRowFocusRequester)
.onFocusChanged {
cardRowHasFocus = it.hasFocus
},
) { ) {
itemsIndexed(eps.episodes) { episodeIndex, episode -> itemsIndexed(eps.episodes) { episodeIndex, episode ->
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
@ -232,9 +247,17 @@ fun SeriesOverviewContent(
} }
}, },
modifier = modifier =
Modifier.ifElse( Modifier
.ifElse(
episodeIndex == position.episodeRowIndex, episodeIndex == position.episodeRowIndex,
Modifier.focusRequester(firstItemFocusRequester), Modifier.focusRequester(firstItemFocusRequester),
).ifElse(
episodeIndex != position.episodeRowIndex,
Modifier
.background(
Color.Black,
shape = RoundedCornerShape(8.dp),
).alpha(dimming),
), ),
interactionSource = interactionSource, interactionSource = interactionSource,
cardHeight = 120.dp, cardHeight = 120.dp,