Minor UI changes (#407)

- Semi-bold the movies or series title on the details pages to match
home page
- Shows total subtitle track count if subtitles disabled
- Remove the springy animations from the play/watch/favorite/more
buttons
- Go back a page when playback finishes and there's nothing left in the
queue

Closes #393
This commit is contained in:
damontecres 2025-12-09 14:00:06 -05:00 committed by GitHub
parent 25fa8a73c2
commit 7644b4c586
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 70 additions and 59 deletions

View file

@ -78,39 +78,37 @@ fun ExpandablePlayButtons(
if (resumePosition > Duration.ZERO) { if (resumePosition > Duration.ZERO) {
item("play") { item("play") {
ExpandablePlayButton( ExpandablePlayButton(
R.string.resume, title = R.string.resume,
resumePosition, resume = resumePosition,
Icons.Default.PlayArrow, icon = Icons.Default.PlayArrow,
playOnClick, onClick = playOnClick,
modifier =
Modifier Modifier
.onFocusChanged(buttonOnFocusChanged) .onFocusChanged(buttonOnFocusChanged)
.focusRequester(firstFocus) .focusRequester(firstFocus),
.animateItem(),
) )
} }
item("restart") { item("restart") {
ExpandablePlayButton( ExpandablePlayButton(
R.string.restart, title = R.string.restart,
Duration.ZERO, resume = Duration.ZERO,
Icons.Default.Refresh, icon = Icons.Default.Refresh,
playOnClick, onClick = playOnClick,
Modifier modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
.onFocusChanged(buttonOnFocusChanged)
.animateItem(),
mirrorIcon = true, mirrorIcon = true,
) )
} }
} else { } else {
item("play") { item("play") {
ExpandablePlayButton( ExpandablePlayButton(
R.string.play, title = R.string.play,
Duration.ZERO, resume = Duration.ZERO,
Icons.Default.PlayArrow, icon = Icons.Default.PlayArrow,
playOnClick, onClick = playOnClick,
modifier =
Modifier Modifier
.onFocusChanged(buttonOnFocusChanged) .onFocusChanged(buttonOnFocusChanged)
.focusRequester(firstFocus) .focusRequester(firstFocus),
.animateItem(),
) )
} }
} }
@ -121,10 +119,7 @@ fun ExpandablePlayButtons(
title = if (watched) R.string.mark_unwatched else R.string.mark_watched, title = if (watched) R.string.mark_unwatched else R.string.mark_watched,
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash, iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
onClick = watchOnClick, onClick = watchOnClick,
modifier = modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
Modifier
.onFocusChanged(buttonOnFocusChanged)
.animateItem(),
) )
} }
@ -135,23 +130,18 @@ fun ExpandablePlayButtons(
iconStringRes = R.string.fa_heart, iconStringRes = R.string.fa_heart,
onClick = favoriteOnClick, onClick = favoriteOnClick,
iconColor = if (favorite) Color.Red else Color.Unspecified, iconColor = if (favorite) Color.Red else Color.Unspecified,
modifier = modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
Modifier
.onFocusChanged(buttonOnFocusChanged)
.animateItem(),
) )
} }
// More button // More button
item("more") { item("more") {
ExpandablePlayButton( ExpandablePlayButton(
R.string.more, title = R.string.more,
Duration.ZERO, resume = Duration.ZERO,
Icons.Default.MoreVert, icon = Icons.Default.MoreVert,
{ moreOnClick.invoke() }, onClick = { moreOnClick.invoke() },
Modifier modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
.onFocusChanged(buttonOnFocusChanged)
.animateItem(),
) )
} }
} }

View file

@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.components
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
@ -23,6 +24,7 @@ fun SeriesName(
text = seriesName ?: "", text = seriesName ?: "",
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.SemiBold,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = modifier, modifier = modifier,

View file

@ -9,7 +9,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -103,8 +106,9 @@ fun VideoStreamDetails(
} }
val audioCount = remember(source) { source?.audioStreamCount ?: 0 } val audioCount = remember(source) { source?.audioStreamCount ?: 0 }
val audio = val audio =
remember(audioCount, audioStream) {
if (audioCount == 0 || audioStream == null) { if (audioCount == 0 || audioStream == null) {
stringResource(R.string.none) context.getString(R.string.none)
} else { } else {
listOfNotNull( listOfNotNull(
languageName(audioStream.language), languageName(audioStream.language),
@ -112,6 +116,7 @@ fun VideoStreamDetails(
audioStream.channelLayout, audioStream.channelLayout,
).joinToString(" ") ).joinToString(" ")
} }
}
StreamLabel( StreamLabel(
text = audio, text = audio,
count = audioCount, count = audioCount,
@ -132,24 +137,30 @@ fun VideoStreamDetails(
remember(source) { remember(source) {
(source?.embeddedSubtitleCount ?: 0) + (source?.externalSubtitlesCount ?: 0) (source?.embeddedSubtitleCount ?: 0) + (source?.externalSubtitlesCount ?: 0)
} }
var disabled by remember { mutableStateOf(false) }
val subtitle = val subtitle =
remember(subtitleCount, subtitleStream, itemPlayback) {
if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) { if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) {
stringResource(R.string.disabled) disabled = true
context.getString(R.string.disabled)
} else if (subtitleCount == 0 || subtitleStream == null) { } else if (subtitleCount == 0 || subtitleStream == null) {
null null
} else { } else {
disabled = false
listOfNotNull( listOfNotNull(
languageName(subtitleStream.language), languageName(subtitleStream.language),
"SDH".takeIf { subtitleStream.isHearingImpaired }, "SDH".takeIf { subtitleStream.isHearingImpaired },
formatSubtitleCodec(subtitleStream.codec), formatSubtitleCodec(subtitleStream.codec),
).joinToString(" ") ).joinToString(" ")
} }
}
subtitle?.let { subtitle?.let {
StreamLabel( StreamLabel(
text = it, text = it,
count = subtitleCount, count = subtitleCount,
icon = R.string.fa_closed_captioning, icon = R.string.fa_closed_captioning,
modifier = Modifier.widthIn(max = 160.dp), modifier = Modifier.widthIn(max = 160.dp),
disabled = disabled,
) )
} }
} }
@ -190,6 +201,7 @@ fun StreamLabel(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@StringRes icon: Int? = null, @StringRes icon: Int? = null,
count: Int = 0, count: Int = 0,
disabled: Boolean = false,
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -222,9 +234,10 @@ fun StreamLabel(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier, modifier = Modifier,
) )
if (count > 1) { val countToUse = if (disabled) count else count - 1
if (countToUse > 0) {
Text( Text(
text = "(+${count - 1})", text = "(+$countToUse)",
maxLines = 1, maxLines = 1,
modifier = Modifier, modifier = Modifier,
) )
@ -248,6 +261,10 @@ private fun StreamLabelPreview() {
StreamLabel("HDR") StreamLabel("HDR")
StreamLabel("H264") StreamLabel("H264")
StreamLabel("AC3 5.1", icon = R.string.fa_volume_high, count = 2) StreamLabel("AC3 5.1", icon = R.string.fa_volume_high, count = 2)
StreamLabel("PGS", count = 1)
StreamLabel("PGS", count = 1, disabled = true)
StreamLabel("PGS", count = 2)
} }
} }
} }

View file

@ -13,6 +13,7 @@ import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -53,6 +54,7 @@ fun MovieDetailsHeader(
text = movie.name ?: "", text = movie.name ?: "",
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.displaySmall, style = MaterialTheme.typography.displaySmall,
fontWeight = FontWeight.SemiBold,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(.75f), modifier = Modifier.fillMaxWidth(.75f),

View file

@ -722,7 +722,7 @@ class PlaybackViewModel
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
nextUp.value = nextItem nextUp.value = nextItem
if (nextItem == null) { if (nextItem == null) {
controllerViewState.showControls() navigationManager.goBack()
} }
} }
} }