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

View file

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

View file

@ -9,7 +9,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -103,14 +106,16 @@ fun VideoStreamDetails(
}
val audioCount = remember(source) { source?.audioStreamCount ?: 0 }
val audio =
if (audioCount == 0 || audioStream == null) {
stringResource(R.string.none)
} else {
listOfNotNull(
languageName(audioStream.language),
formatAudioCodec(context, audioStream.codec, audioStream.profile),
audioStream.channelLayout,
).joinToString(" ")
remember(audioCount, audioStream) {
if (audioCount == 0 || audioStream == null) {
context.getString(R.string.none)
} else {
listOfNotNull(
languageName(audioStream.language),
formatAudioCodec(context, audioStream.codec, audioStream.profile),
audioStream.channelLayout,
).joinToString(" ")
}
}
StreamLabel(
text = audio,
@ -132,17 +137,22 @@ fun VideoStreamDetails(
remember(source) {
(source?.embeddedSubtitleCount ?: 0) + (source?.externalSubtitlesCount ?: 0)
}
var disabled by remember { mutableStateOf(false) }
val subtitle =
if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) {
stringResource(R.string.disabled)
} else if (subtitleCount == 0 || subtitleStream == null) {
null
} else {
listOfNotNull(
languageName(subtitleStream.language),
"SDH".takeIf { subtitleStream.isHearingImpaired },
formatSubtitleCodec(subtitleStream.codec),
).joinToString(" ")
remember(subtitleCount, subtitleStream, itemPlayback) {
if (itemPlayback?.subtitleIndex == TrackIndex.DISABLED) {
disabled = true
context.getString(R.string.disabled)
} else if (subtitleCount == 0 || subtitleStream == null) {
null
} else {
disabled = false
listOfNotNull(
languageName(subtitleStream.language),
"SDH".takeIf { subtitleStream.isHearingImpaired },
formatSubtitleCodec(subtitleStream.codec),
).joinToString(" ")
}
}
subtitle?.let {
StreamLabel(
@ -150,6 +160,7 @@ fun VideoStreamDetails(
count = subtitleCount,
icon = R.string.fa_closed_captioning,
modifier = Modifier.widthIn(max = 160.dp),
disabled = disabled,
)
}
}
@ -190,6 +201,7 @@ fun StreamLabel(
modifier: Modifier = Modifier,
@StringRes icon: Int? = null,
count: Int = 0,
disabled: Boolean = false,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@ -222,9 +234,10 @@ fun StreamLabel(
overflow = TextOverflow.Ellipsis,
modifier = Modifier,
)
if (count > 1) {
val countToUse = if (disabled) count else count - 1
if (countToUse > 0) {
Text(
text = "(+${count - 1})",
text = "(+$countToUse)",
maxLines = 1,
modifier = Modifier,
)
@ -248,6 +261,10 @@ private fun StreamLabelPreview() {
StreamLabel("HDR")
StreamLabel("H264")
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.res.stringResource
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.unit.Dp
import androidx.compose.ui.unit.dp
@ -53,6 +54,7 @@ fun MovieDetailsHeader(
text = movie.name ?: "",
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.displaySmall,
fontWeight = FontWeight.SemiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(.75f),

View file

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