Restore focus on search

This commit is contained in:
Damontecres 2025-10-11 15:12:23 -04:00
parent 3afc4f022b
commit b30ddab0fc
No known key found for this signature in database
2 changed files with 73 additions and 29 deletions

View file

@ -37,13 +37,11 @@ import com.github.damontecres.dolphin.ui.components.StarRating
import com.github.damontecres.dolphin.ui.components.StarRatingPrecision import com.github.damontecres.dolphin.ui.components.StarRatingPrecision
import com.github.damontecres.dolphin.ui.components.TitleValueText import com.github.damontecres.dolphin.ui.components.TitleValueText
import com.github.damontecres.dolphin.ui.isNotNullOrBlank import com.github.damontecres.dolphin.ui.isNotNullOrBlank
import com.github.damontecres.dolphin.ui.letNotEmpty
import com.github.damontecres.dolphin.ui.playOnClickSound import com.github.damontecres.dolphin.ui.playOnClickSound
import com.github.damontecres.dolphin.ui.playSoundOnFocus import com.github.damontecres.dolphin.ui.playSoundOnFocus
import com.github.damontecres.dolphin.ui.roundMinutes import com.github.damontecres.dolphin.ui.roundMinutes
import com.github.damontecres.dolphin.ui.timeRemaining import com.github.damontecres.dolphin.ui.timeRemaining
import org.jellyfin.sdk.model.api.MediaStreamType import org.jellyfin.sdk.model.api.MediaStreamType
import org.jellyfin.sdk.model.api.PersonKind
import org.jellyfin.sdk.model.extensions.ticks import org.jellyfin.sdk.model.extensions.ticks
@Composable @Composable
@ -167,7 +165,7 @@ fun MovieDetailsHeader(
TitleValueText( TitleValueText(
stringResource(R.string.video), stringResource(R.string.video),
it, it,
modifier = Modifier, modifier = Modifier.widthIn(max = 200.dp),
) )
} }
dto.mediaStreams dto.mediaStreams
@ -183,6 +181,7 @@ fun MovieDetailsHeader(
TitleValueText( TitleValueText(
stringResource(R.string.audio), stringResource(R.string.audio),
it, it,
modifier = Modifier.widthIn(max = 200.dp),
) )
} }
} }
@ -195,18 +194,18 @@ fun MovieDetailsHeader(
TitleValueText( TitleValueText(
"Subtitles", "Subtitles",
it, it,
modifier = Modifier.widthIn(max = 64.dp), modifier = Modifier.widthIn(max = 120.dp),
) )
} }
} }
// TODO add writers, studio, etc to overview dialog // TODO add writers, studio, etc to overview dialog
dto.people?.firstOrNull { it.type == PersonKind.DIRECTOR }?.name?.let { // dto.people?.firstOrNull { it.type == PersonKind.DIRECTOR }?.name?.let {
TitleValueText( // TitleValueText(
stringResource(R.string.director), // stringResource(R.string.director),
it, // it,
modifier = Modifier.widthIn(max = 80.dp), // modifier = Modifier.widthIn(max = 80.dp),
) // )
} // }
// dto.studios?.letNotEmpty { // dto.studios?.letNotEmpty {
// TitleValueText( // TitleValueText(
// stringResource(R.string.studios), // stringResource(R.string.studios),
@ -214,13 +213,13 @@ fun MovieDetailsHeader(
// modifier = Modifier.widthIn(max = 80.dp), // modifier = Modifier.widthIn(max = 80.dp),
// ) // )
// } // }
dto.genres?.letNotEmpty { // dto.genres?.letNotEmpty {
TitleValueText( // TitleValueText(
stringResource(R.string.genres), // stringResource(R.string.genres),
it.joinToString(", "), // it.joinToString(", "),
modifier = Modifier.widthIn(max = 80.dp), // modifier = Modifier.widthIn(max = 80.dp),
) // )
} // }
} }
} }
} }

View file

@ -31,6 +31,9 @@ import com.github.damontecres.dolphin.ui.cards.EpisodeCard
import com.github.damontecres.dolphin.ui.cards.ItemRow import com.github.damontecres.dolphin.ui.cards.ItemRow
import com.github.damontecres.dolphin.ui.cards.SeasonCard import com.github.damontecres.dolphin.ui.cards.SeasonCard
import com.github.damontecres.dolphin.ui.components.SearchEditTextBox import com.github.damontecres.dolphin.ui.components.SearchEditTextBox
import com.github.damontecres.dolphin.ui.data.RowColumn
import com.github.damontecres.dolphin.ui.data.RowColumnSaver
import com.github.damontecres.dolphin.ui.ifElse
import com.github.damontecres.dolphin.ui.isNotNullOrBlank import com.github.damontecres.dolphin.ui.isNotNullOrBlank
import com.github.damontecres.dolphin.ui.nav.NavigationManager import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.ui.tryRequestFocus import com.github.damontecres.dolphin.ui.tryRequestFocus
@ -58,7 +61,13 @@ class SearchViewModel
val series = MutableLiveData<List<BaseItem?>>(listOf()) val series = MutableLiveData<List<BaseItem?>>(listOf())
val episodes = MutableLiveData<List<BaseItem?>>(listOf()) val episodes = MutableLiveData<List<BaseItem?>>(listOf())
private var currentQuery: String? = null
fun search(query: String?) { fun search(query: String?) {
if (currentQuery == query) {
return
}
currentQuery = query
movies.value = listOf() movies.value = listOf()
series.value = listOf() series.value = listOf()
episodes.value = listOf() episodes.value = listOf()
@ -117,6 +126,10 @@ class SearchViewModel
} }
} }
private const val MOVIE_ROW = 0
private const val SERIES_ROW = 1
private const val EPISODE_ROW = 2
@Composable @Composable
fun SearchPage( fun SearchPage(
userPreferences: UserPreferences, userPreferences: UserPreferences,
@ -128,16 +141,20 @@ fun SearchPage(
val episodes by viewModel.episodes.observeAsState(listOf()) val episodes by viewModel.episodes.observeAsState(listOf())
var query by rememberSaveable { mutableStateOf("") } var query by rememberSaveable { mutableStateOf("") }
val searchFocusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val resultsFocusRequester = remember { FocusRequester() }
var position by rememberSaveable(stateSaver = RowColumnSaver) {
mutableStateOf(
RowColumn(-1, -1),
)
}
LaunchedEffect(query) { LaunchedEffect(query) {
delay(750L) delay(750L)
viewModel.search(query) viewModel.search(query)
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
// TODO focus on back to results when available focusRequester.tryRequestFocus()
searchFocusRequester.tryRequestFocus()
} }
LazyColumn( LazyColumn(
@ -158,7 +175,11 @@ fun SearchPage(
onSearchClick = { onSearchClick = {
viewModel.search(query) viewModel.search(query)
}, },
modifier = Modifier.focusRequester(searchFocusRequester), modifier =
Modifier.ifElse(
position.row < MOVIE_ROW,
Modifier.focusRequester(focusRequester),
),
) )
} }
} }
@ -175,10 +196,18 @@ fun SearchPage(
cardContent = @Composable { index, item, mod, onClick, onLongClick -> cardContent = @Composable { index, item, mod, onClick, onLongClick ->
SeasonCard( SeasonCard(
item = item, item = item,
onClick = onClick, onClick = {
position = RowColumn(MOVIE_ROW, index)
onClick.invoke()
},
onLongClick = onLongClick, onLongClick = onLongClick,
imageHeight = Cards.height2x3, imageHeight = Cards.height2x3,
modifier = mod, modifier =
mod
.ifElse(
position.row == MOVIE_ROW && position.column == index,
Modifier.focusRequester(focusRequester),
),
) )
}, },
) )
@ -197,10 +226,17 @@ fun SearchPage(
cardContent = @Composable { index, item, mod, onClick, onLongClick -> cardContent = @Composable { index, item, mod, onClick, onLongClick ->
SeasonCard( SeasonCard(
item = item, item = item,
onClick = onClick, onClick = {
position = RowColumn(SERIES_ROW, index)
onClick.invoke()
},
onLongClick = onLongClick, onLongClick = onLongClick,
imageHeight = Cards.height2x3, imageHeight = Cards.height2x3,
modifier = mod, modifier =
mod.ifElse(
position.row == SERIES_ROW && position.column == index,
Modifier.focusRequester(focusRequester),
),
) )
}, },
) )
@ -219,10 +255,19 @@ fun SearchPage(
cardContent = @Composable { index, item, mod, onClick, onLongClick -> cardContent = @Composable { index, item, mod, onClick, onLongClick ->
EpisodeCard( EpisodeCard(
item = item, item = item,
onClick = onClick, onClick = {
position = RowColumn(EPISODE_ROW, index)
onClick.invoke()
},
onLongClick = onLongClick, onLongClick = onLongClick,
imageHeight = 140.dp, imageHeight = 140.dp,
modifier = mod.padding(horizontal = 8.dp), modifier =
mod
.padding(horizontal = 8.dp)
.ifElse(
position.row == EPISODE_ROW && position.column == index,
Modifier.focusRequester(focusRequester),
),
) )
}, },
) )