Scale hardcoded fontSize sites by Text size level

Adds a scaledSp(baseSp: Int) composable helper that returns a TextUnit
scaled by the active text-size level. The lookup is a hand-mapped
DisplaySizeLevel.scaledSp(Int) table for the known base sp values
(integer outputs at every cell, no fractional sp). Unknown base sp
values fall back to integer-rounded percent math instead of throwing,
so the helper is safe to call from anywhere.

scaledSp reads from LocalInterfaceCustomization.current.textSizeLevel
so the same composition local that already carries the user's interface
preferences also drives text scaling.

33 hardcoded fontSize = N.sp call sites across cards, components,
playback overlay, nav, setup, and livetv go through scaledSp. This
covers both plain text and FontAwesome icon-glyph sites: at the Default
level the lookup returns the exact base sp, so today's rendered sizes
are byte-identical. PlaybackController's file-level titleTextSize and
subtitleTextSize constants are inlined as scaledSp(28) and scaledSp(18),
keeping the rendering and PlaybackOverlay's height-offset math in lockstep
at every level.

The v1 scaledDensity helper in InterfaceHelpers.kt and its tests are
deleted - they had no callers after MainContent's density override was
removed. The InterfaceCustomization derived-getter doc comment is
retuned to v2-valid percent values.
This commit is contained in:
Justin Visser 2026-05-28 12:49:55 +02:00
parent f039be0495
commit ca8ed52cf0
26 changed files with 138 additions and 113 deletions

View file

@ -1,5 +1,8 @@
package com.github.damontecres.wholphin.preferences package com.github.damontecres.wholphin.preferences
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
val displaySizeLevelDisplayOrder: List<DisplaySizeLevel> = val displaySizeLevelDisplayOrder: List<DisplaySizeLevel> =
listOf( listOf(
DisplaySizeLevel.EXTRA_SMALL, DisplaySizeLevel.EXTRA_SMALL,
@ -8,6 +11,70 @@ val displaySizeLevelDisplayOrder: List<DisplaySizeLevel> =
DisplaySizeLevel.LARGE, DisplaySizeLevel.LARGE,
) )
fun DisplaySizeLevel.scaledSp(baseSp: Int): TextUnit {
if (this == DisplaySizeLevel.DEFAULT || this == DisplaySizeLevel.UNRECOGNIZED) {
return baseSp.sp
}
val mapped =
when (this) {
DisplaySizeLevel.EXTRA_SMALL -> {
when (baseSp) {
10 -> 9
12 -> 11
13 -> 11
14 -> 12
16 -> 14
18 -> 16
20 -> 18
24 -> 21
28 -> 25
56 -> 49
64 -> 56
else -> null
}
}
DisplaySizeLevel.SMALL -> {
when (baseSp) {
10 -> 9
12 -> 11
13 -> 12
14 -> 13
16 -> 15
18 -> 17
20 -> 19
24 -> 23
28 -> 26
56 -> 53
64 -> 60
else -> null
}
}
DisplaySizeLevel.LARGE -> {
when (baseSp) {
10 -> 11
12 -> 13
13 -> 14
14 -> 15
16 -> 17
18 -> 19
20 -> 22
24 -> 26
28 -> 30
56 -> 60
64 -> 69
else -> null
}
}
else -> {
null
}
}
return mapped?.sp ?: ((baseSp * textPercent() + 50) / 100).sp
}
fun DisplaySizeLevel.textPercent(): Int = fun DisplaySizeLevel.textPercent(): Int =
when (this) { when (this) {
DisplaySizeLevel.EXTRA_SMALL -> 88 DisplaySizeLevel.EXTRA_SMALL -> 88

View file

@ -34,7 +34,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.isSpecified import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Card import androidx.tv.material3.Card
import androidx.tv.material3.CardDefaults import androidx.tv.material3.CardDefaults
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
@ -48,6 +47,7 @@ import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.FontAwesome import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.LocalImageUrlService import com.github.damontecres.wholphin.ui.LocalImageUrlService
import com.github.damontecres.wholphin.ui.enableMarquee import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.util.scaledSp
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
/** /**
@ -179,7 +179,7 @@ fun BannerCard(
.padding(8.dp), .padding(8.dp),
color = colorResource(android.R.color.holo_red_light), color = colorResource(android.R.color.holo_red_light),
text = stringResource(R.string.fa_heart), text = stringResource(R.string.fa_heart),
fontSize = 16.sp, fontSize = scaledSp(16),
fontFamily = FontAwesome, fontFamily = FontAwesome,
) )
} }

View file

@ -30,7 +30,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Card import androidx.tv.material3.Card
import androidx.tv.material3.CardDefaults import androidx.tv.material3.CardDefaults
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
@ -46,6 +45,7 @@ import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.enableMarquee import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.scaledSp
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@Composable @Composable
@ -157,7 +157,7 @@ fun DiscoverItemCard(
Text( Text(
text = pluralStringResource(it, 1), text = pluralStringResource(it, 1),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
fontSize = 10.sp, fontSize = scaledSp(10),
modifier = modifier =
Modifier Modifier
.align(Alignment.TopStart) .align(Alignment.TopStart)
@ -224,7 +224,7 @@ fun PendingIndicator(modifier: Modifier = Modifier) {
Text( Text(
text = stringResource(R.string.fa_bell), text = stringResource(R.string.fa_bell),
fontFamily = FontAwesome, fontFamily = FontAwesome,
fontSize = 10.sp, fontSize = scaledSp(10),
color = AppColors.Discover.Yellow, color = AppColors.Discover.Yellow,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
) )
@ -249,7 +249,7 @@ fun AvailableIndicator(modifier: Modifier = Modifier) {
Text( Text(
text = stringResource(R.string.fa_check), text = stringResource(R.string.fa_check),
fontFamily = FontAwesome, fontFamily = FontAwesome,
fontSize = 10.sp, fontSize = scaledSp(10),
color = Color.White, color = Color.White,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
) )

View file

@ -29,7 +29,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
@ -41,6 +40,7 @@ import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.LocalImageUrlService import com.github.damontecres.wholphin.ui.LocalImageUrlService
import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.logCoilError import com.github.damontecres.wholphin.ui.logCoilError
import com.github.damontecres.wholphin.ui.util.scaledSp
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
/** /**
@ -166,7 +166,7 @@ fun BoxScope.ItemCardImageFallback(
Text( Text(
text = name, text = name,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 14.sp, fontSize = scaledSp(14),
modifier = modifier =
Modifier Modifier
.padding(8.dp) .padding(8.dp)
@ -222,7 +222,6 @@ fun ItemCardImageOverlay(
text = numberOfVersions.toString(), text = numberOfVersions.toString(),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
// fontSize = 16.sp,
modifier = Modifier.padding(4.dp), modifier = Modifier.padding(4.dp),
) )
} }
@ -255,7 +254,6 @@ fun ItemCardImageOverlay(
text = unwatchedCount.toString(), text = unwatchedCount.toString(),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
// fontSize = 16.sp,
modifier = Modifier.padding(4.dp), modifier = Modifier.padding(4.dp),
) )
} }
@ -280,7 +278,7 @@ fun ItemCardImageOverlay(
@Composable @Composable
fun FavoriteIndicator( fun FavoriteIndicator(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
fontSize: TextUnit = 20.sp, fontSize: TextUnit = scaledSp(20),
) = Text( ) = Text(
color = colorResource(android.R.color.holo_red_light), color = colorResource(android.R.color.holo_red_light),
text = stringResource(R.string.fa_heart), text = stringResource(R.string.fa_heart),

View file

@ -32,7 +32,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Border import androidx.tv.material3.Border
import androidx.tv.material3.Card import androidx.tv.material3.Card
import androidx.tv.material3.CardDefaults import androidx.tv.material3.CardDefaults
@ -45,6 +44,7 @@ import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.enableMarquee import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.jellyfin.sdk.model.UUID import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.PersonKind import org.jellyfin.sdk.model.api.PersonKind
@ -153,7 +153,7 @@ fun PersonCard(
text = stringResource(R.string.fa_user), text = stringResource(R.string.fa_user),
fontFamily = FontAwesome, fontFamily = FontAwesome,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 64.sp, fontSize = scaledSp(64),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = modifier =
Modifier Modifier

View file

@ -31,7 +31,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Icon import androidx.tv.material3.Icon
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
@ -54,6 +53,7 @@ import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.scaledSp
import java.util.UUID import java.util.UUID
/** /**
@ -399,7 +399,7 @@ fun ExpandableFilterButton(
text = stringResource(R.string.fa_filter), text = stringResource(R.string.fa_filter),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = iconColor, color = iconColor,
fontSize = 16.sp, fontSize = scaledSp(16),
fontFamily = FontAwesome, fontFamily = FontAwesome,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),

View file

@ -46,7 +46,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Icon import androidx.tv.material3.Icon
import androidx.tv.material3.LocalContentColor import androidx.tv.material3.LocalContentColor
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
@ -59,6 +58,7 @@ import com.github.damontecres.wholphin.ui.data.SortAndDirection
import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
import org.jellyfin.sdk.model.api.ItemSortBy import org.jellyfin.sdk.model.api.ItemSortBy
import org.jellyfin.sdk.model.api.SortOrder import org.jellyfin.sdk.model.api.SortOrder
import kotlin.time.Duration import kotlin.time.Duration
@ -329,7 +329,7 @@ fun ExpandableFaButton(
text = stringResource(iconStringRes), text = stringResource(iconStringRes),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = iconColor, color = iconColor,
fontSize = 16.sp, fontSize = scaledSp(16),
fontFamily = FontAwesome, fontFamily = FontAwesome,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),

View file

@ -4,9 +4,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight 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.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.ui.util.scaledSp
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@Composable @Composable
@ -34,7 +34,7 @@ fun EpisodeName(
text = episodeName ?: "", text = episodeName ?: "",
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.headlineSmall, style = MaterialTheme.typography.headlineSmall,
fontSize = 20.sp, fontSize = scaledSp(20),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = modifier, modifier = modifier,

View file

@ -36,12 +36,12 @@ import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.scaledSp
import timber.log.Timber import timber.log.Timber
@Composable @Composable
@ -147,7 +147,7 @@ fun Tab(
) { ) {
Text( Text(
text = title, text = title,
fontSize = 16.sp, fontSize = scaledSp(16),
color = contentColor, color = contentColor,
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
) )

View file

@ -7,10 +7,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.ui.util.LocalClock import com.github.damontecres.wholphin.ui.util.LocalClock
import com.github.damontecres.wholphin.ui.util.scaledSp
/** /**
* Displays the [LocalClock] in the upper right corner of the parent [androidx.compose.foundation.layout.Box] * Displays the [LocalClock] in the upper right corner of the parent [androidx.compose.foundation.layout.Box]
@ -20,7 +20,7 @@ fun BoxScope.TimeDisplay(modifier: Modifier = Modifier) {
val timeString by LocalClock.current.timeString val timeString by LocalClock.current.timeString
Text( Text(
text = timeString, text = timeString,
fontSize = 18.sp, fontSize = scaledSp(18),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
modifier = modifier =

View file

@ -23,7 +23,6 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight 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.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.ProvideTextStyle import androidx.tv.material3.ProvideTextStyle
import androidx.tv.material3.Text import androidx.tv.material3.Text
@ -41,6 +40,7 @@ import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatAudioCodec
import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatSubtitleCodec import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatSubtitleCodec
import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatVideoRange import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatVideoRange
import com.github.damontecres.wholphin.ui.util.StreamFormatting.resolutionString import com.github.damontecres.wholphin.ui.util.StreamFormatting.resolutionString
import com.github.damontecres.wholphin.ui.util.scaledSp
import com.github.damontecres.wholphin.util.languageName import com.github.damontecres.wholphin.util.languageName
import org.jellyfin.sdk.model.api.MediaSourceInfo import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.jellyfin.sdk.model.api.MediaStream import org.jellyfin.sdk.model.api.MediaStream
@ -185,7 +185,7 @@ fun StreamLabel(
ProvideTextStyle( ProvideTextStyle(
TextStyle( TextStyle(
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
fontSize = 12.sp, fontSize = scaledSp(12),
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
), ),
) { ) {

View file

@ -43,7 +43,6 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.tv.material3.ButtonDefaults import androidx.tv.material3.ButtonDefaults
@ -54,13 +53,12 @@ import androidx.tv.material3.Text
import androidx.tv.material3.surfaceColorAtElevation import androidx.tv.material3.surfaceColorAtElevation
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.FontAwesome import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.util.scaledSp
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
private const val ERROR_AUTO_DISMISS_DELAY_MS = 3000L private const val ERROR_AUTO_DISMISS_DELAY_MS = 3000L
private const val SOUND_LEVEL_SCALE_FACTOR = 0.15f private const val SOUND_LEVEL_SCALE_FACTOR = 0.15f
private val BUBBLE_SIZE = 160.dp private val BUBBLE_SIZE = 160.dp
private val MIC_ICON_FONT_SIZE = 56.sp
private val BUTTON_ICON_FONT_SIZE = 20.sp
private val CONTENT_SPACING = 48.dp private val CONTENT_SPACING = 48.dp
private val HORIZONTAL_PADDING = 64.dp private val HORIZONTAL_PADDING = 64.dp
private val DISMISS_HINT_BOTTOM_PADDING = 32.dp private val DISMISS_HINT_BOTTOM_PADDING = 32.dp
@ -174,7 +172,7 @@ fun VoiceSearchButton(
Text( Text(
text = stringResource(R.string.fa_microphone), text = stringResource(R.string.fa_microphone),
fontFamily = FontAwesome, fontFamily = FontAwesome,
fontSize = BUTTON_ICON_FONT_SIZE, fontSize = scaledSp(20),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.semantics { contentDescription = voiceSearchDesc }, modifier = Modifier.semantics { contentDescription = voiceSearchDesc },
) )
@ -347,7 +345,7 @@ private fun VoiceSearchOverlay(
Text( Text(
text = stringResource(R.string.fa_microphone), text = stringResource(R.string.fa_microphone),
fontFamily = FontAwesome, fontFamily = FontAwesome,
fontSize = MIC_ICON_FONT_SIZE, fontSize = scaledSp(56),
color = onPrimaryColor, color = onPrimaryColor,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.semantics { contentDescription = voiceSearchDesc }, modifier = Modifier.semantics { contentDescription = voiceSearchDesc },

View file

@ -19,7 +19,6 @@ import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
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.sp
import androidx.tv.material3.ClickableSurfaceDefaults import androidx.tv.material3.ClickableSurfaceDefaults
import androidx.tv.material3.LocalContentColor import androidx.tv.material3.LocalContentColor
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
@ -29,6 +28,7 @@ import androidx.tv.material3.surfaceColorAtElevation
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.FontAwesome import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.util.scaledSp
import java.time.LocalDateTime import java.time.LocalDateTime
@Composable @Composable
@ -87,7 +87,7 @@ fun Program(
text = stringResource(R.string.fa_caret_left), text = stringResource(R.string.fa_caret_left),
fontFamily = FontAwesome, fontFamily = FontAwesome,
color = LocalContentColor.current, color = LocalContentColor.current,
fontSize = 16.sp, fontSize = scaledSp(16),
modifier = modifier =
Modifier Modifier
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
@ -104,7 +104,7 @@ fun Program(
Text( Text(
text = title, text = title,
color = LocalContentColor.current, color = LocalContentColor.current,
fontSize = 16.sp, fontSize = scaledSp(16),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier, modifier = Modifier,
@ -120,7 +120,7 @@ fun Program(
Text( Text(
text = it, text = it,
color = LocalContentColor.current, color = LocalContentColor.current,
fontSize = 14.sp, fontSize = scaledSp(14),
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier, modifier = Modifier,
) )
@ -197,7 +197,7 @@ fun Channel(
Text( Text(
text = it, text = it,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
fontSize = if (showImage) 10.sp else 16.sp, fontSize = if (showImage) scaledSp(10) else scaledSp(16),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier, modifier = Modifier,
@ -209,7 +209,7 @@ fun Channel(
Text( Text(
color = colorResource(android.R.color.holo_red_light), color = colorResource(android.R.color.holo_red_light),
text = stringResource(R.string.fa_heart), text = stringResource(R.string.fa_heart),
fontSize = 16.sp, fontSize = scaledSp(16),
fontFamily = FontAwesome, fontFamily = FontAwesome,
modifier = modifier =
Modifier Modifier

View file

@ -24,7 +24,6 @@ import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.ui.compose.state.rememberNextButtonState import androidx.media3.ui.compose.state.rememberNextButtonState
@ -48,6 +47,7 @@ import com.github.damontecres.wholphin.ui.playback.overlay.PlaybackFaButton
import com.github.damontecres.wholphin.ui.playback.overlay.buttonSpacing import com.github.damontecres.wholphin.ui.playback.overlay.buttonSpacing
import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
@ -176,7 +176,7 @@ fun ShuffleButton(
) { ) {
Text( Text(
text = stringResource(R.string.fa_shuffle), text = stringResource(R.string.fa_shuffle),
fontSize = 18.sp, fontSize = scaledSp(18),
fontFamily = FontAwesome, fontFamily = FontAwesome,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
color = color =
@ -228,7 +228,7 @@ fun RepeatButton(
) { ) {
Text( Text(
text = stringResource(R.string.fa_repeat), text = stringResource(R.string.fa_repeat),
fontSize = 18.sp, fontSize = scaledSp(18),
fontFamily = FontAwesome, fontFamily = FontAwesome,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
color = color =
@ -253,7 +253,7 @@ fun RepeatButton(
if (repeatMode == Player.REPEAT_MODE_ONE) { if (repeatMode == Player.REPEAT_MODE_ONE) {
Text( Text(
text = "1", text = "1",
fontSize = 10.sp, fontSize = scaledSp(10),
color = MaterialTheme.colorScheme.surface, color = MaterialTheme.colorScheme.surface,
modifier = modifier =
Modifier Modifier

View file

@ -53,7 +53,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.findViewTreeViewModelStoreOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner
@ -89,6 +88,7 @@ import com.github.damontecres.wholphin.ui.spacedByWithFooter
import com.github.damontecres.wholphin.ui.theme.LocalTheme import com.github.damontecres.wholphin.ui.theme.LocalTheme
import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.scaledSp
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -717,7 +717,7 @@ fun NavigationDrawerScope.NavItem(
Text( Text(
text = stringResource(icon), text = stringResource(icon),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
fontSize = 16.sp, fontSize = scaledSp(16),
fontFamily = FontAwesome, fontFamily = FontAwesome,
color = color, color = color,
modifier = Modifier, modifier = Modifier,

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.res.stringResource
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
import androidx.compose.ui.unit.sp
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
@ -35,6 +34,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.ui.getTimeFormatter import com.github.damontecres.wholphin.ui.getTimeFormatter
import com.github.damontecres.wholphin.ui.playback.ControllerViewState import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType
import com.github.damontecres.wholphin.ui.util.scaledSp
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import org.jellyfin.sdk.model.api.MediaSegmentDto import org.jellyfin.sdk.model.api.MediaSegmentDto
@ -121,9 +121,6 @@ fun PlaybackController(
} }
} }
internal val titleTextSize = 28.sp
internal val subtitleTextSize = 18.sp
/** /**
* A wrapper for the playback controls to show title and other information, plus the actual controls * A wrapper for the playback controls to show title and other information, plus the actual controls
* *
@ -175,7 +172,7 @@ fun Controller(
Text( Text(
text = it, text = it,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontSize = titleTextSize, fontSize = scaledSp(28),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.MiddleEllipsis, overflow = TextOverflow.MiddleEllipsis,
modifier = Modifier.fillMaxWidth(.75f), modifier = Modifier.fillMaxWidth(.75f),
@ -192,7 +189,7 @@ fun Controller(
Text( Text(
text = it, text = it,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
fontSize = subtitleTextSize, fontSize = scaledSp(18),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.MiddleEllipsis, overflow = TextOverflow.MiddleEllipsis,
modifier = Modifier.fillMaxWidth(.75f), modifier = Modifier.fillMaxWidth(.75f),

View file

@ -49,7 +49,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.DialogWindowProvider import androidx.compose.ui.window.DialogWindowProvider
@ -79,6 +78,7 @@ import com.github.damontecres.wholphin.ui.skipStringRes
import com.github.damontecres.wholphin.ui.theme.LocalTheme import com.github.damontecres.wholphin.ui.theme.LocalTheme
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.scaledSp
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@ -497,7 +497,7 @@ fun PlaybackFaButton(
) { ) {
Text( Text(
text = stringResource(iconRes), text = stringResource(iconRes),
fontSize = 18.sp, fontSize = scaledSp(18),
fontFamily = FontAwesome, fontFamily = FontAwesome,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
color = color =

View file

@ -18,7 +18,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.media3.common.Format import androidx.media3.common.Format
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
@ -33,6 +32,7 @@ import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.playback.AnalyticsState import com.github.damontecres.wholphin.ui.playback.AnalyticsState
import com.github.damontecres.wholphin.ui.playback.CurrentPlayback import com.github.damontecres.wholphin.ui.playback.CurrentPlayback
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
import com.github.damontecres.wholphin.util.TrackSupport import com.github.damontecres.wholphin.util.TrackSupport
import com.github.damontecres.wholphin.util.TrackSupportReason import com.github.damontecres.wholphin.util.TrackSupportReason
import com.github.damontecres.wholphin.util.TrackType import com.github.damontecres.wholphin.util.TrackType
@ -85,7 +85,7 @@ fun PlaybackDebugOverlay(
} }
val textStyle = val textStyle =
MaterialTheme.typography.bodySmall.copy( MaterialTheme.typography.bodySmall.copy(
fontSize = 10.sp, fontSize = scaledSp(10),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
) )

View file

@ -61,6 +61,7 @@ import com.github.damontecres.wholphin.ui.playback.AnalyticsState
import com.github.damontecres.wholphin.ui.playback.ControllerViewState import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.github.damontecres.wholphin.ui.playback.CurrentPlayback import com.github.damontecres.wholphin.ui.playback.CurrentPlayback
import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType
import com.github.damontecres.wholphin.ui.util.scaledSp
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
import org.jellyfin.sdk.model.api.MediaSegmentDto import org.jellyfin.sdk.model.api.MediaSegmentDto
import org.jellyfin.sdk.model.api.TrickplayInfo import org.jellyfin.sdk.model.api.TrickplayInfo
@ -105,13 +106,15 @@ fun PlaybackOverlay(
val density = LocalDensity.current val density = LocalDensity.current
val titleSp = scaledSp(28)
val subtitleSp = scaledSp(18)
val titleHeight = val titleHeight =
remember(item?.title) { remember(item?.title, titleSp) {
if (item?.title.isNotNullOrBlank()) with(density) { titleTextSize.toDp() } else 0.dp if (item?.title.isNotNullOrBlank()) with(density) { titleSp.toDp() } else 0.dp
} }
val subtitleHeight = val subtitleHeight =
remember(item?.subtitleLong) { remember(item?.subtitleLong, subtitleSp) {
if (item?.subtitleLong.isNotNullOrBlank()) with(density) { subtitleTextSize.toDp() } else 0.dp if (item?.subtitleLong.isNotNullOrBlank()) with(density) { subtitleSp.toDp() } else 0.dp
} }
// This will be calculated after composition // This will be calculated after composition

View file

@ -18,11 +18,11 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale import androidx.compose.ui.draw.scale
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.util.scaledSp
import kotlin.math.abs import kotlin.math.abs
/** /**
@ -63,7 +63,7 @@ fun SkipIndicator(
Text( Text(
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.onBackground, color = MaterialTheme.colorScheme.onBackground,
fontSize = 13.sp, fontSize = scaledSp(13),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
text = abs(durationMs / 1000).toString(), text = abs(durationMs / 1000).toString(),
) )

View file

@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -59,6 +58,7 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.main.settings.MoveDirection import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.RememberTabManager import com.github.damontecres.wholphin.util.RememberTabManager
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -275,7 +275,7 @@ fun MoveButton(
) { ) {
Text( Text(
text = stringResource(icon), text = stringResource(icon),
fontSize = 16.sp, fontSize = scaledSp(16),
fontFamily = FontAwesome, fontFamily = FontAwesome,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),

View file

@ -36,7 +36,6 @@ import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.key.type import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -60,6 +59,7 @@ import com.github.damontecres.wholphin.ui.dimAndBlur
import com.github.damontecres.wholphin.ui.formatBytes import com.github.damontecres.wholphin.ui.formatBytes
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.Version import com.github.damontecres.wholphin.util.Version
@ -369,7 +369,7 @@ fun DownloadDialog(
) { ) {
Text( Text(
text = stringResource(R.string.downloading), text = stringResource(R.string.downloading),
fontSize = 24.sp, fontSize = scaledSp(24),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
) )
if (progress != null) { if (progress != null) {
@ -396,7 +396,7 @@ fun DownloadDialog(
val size = formatBytes(contentLength) val size = formatBytes(contentLength)
Text( Text(
text = "$bytes / $size", text = "$bytes / $size",
fontSize = 18.sp, fontSize = scaledSp(18),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
) )
} }

View file

@ -27,7 +27,6 @@ import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
@ -38,6 +37,7 @@ import com.github.damontecres.wholphin.ui.components.Button
import com.github.damontecres.wholphin.ui.components.TextButton import com.github.damontecres.wholphin.ui.components.TextButton
import com.github.damontecres.wholphin.ui.playback.isEnterKey import com.github.damontecres.wholphin.ui.playback.isEnterKey
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.scaledSp
@Composable @Composable
fun PinEntry( fun PinEntry(
@ -101,7 +101,7 @@ fun PinEntry(
) )
Text( Text(
text = stringResource(R.string.will_remove_pin), text = stringResource(R.string.will_remove_pin),
fontSize = 10.sp, fontSize = scaledSp(10),
) )
} }
} }

View file

@ -39,7 +39,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
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.sp
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.tv.material3.Border import androidx.tv.material3.Border
import androidx.tv.material3.Button import androidx.tv.material3.Button
@ -60,6 +59,7 @@ import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.scaledSp
import java.util.UUID import java.util.UUID
/** /**
@ -333,7 +333,7 @@ fun UserIconCardImage(
Text( Text(
text = firstLetter, text = firstLetter,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
fontSize = 14.sp, fontSize = scaledSp(14),
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,

View file

@ -1,11 +1,13 @@
package com.github.damontecres.wholphin.ui.util package com.github.damontecres.wholphin.ui.util
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Density import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.preferences.AppSliderPreference import com.github.damontecres.wholphin.preferences.AppSliderPreference
import com.github.damontecres.wholphin.preferences.scaledSp
import com.github.damontecres.wholphin.ui.AspectRatio import com.github.damontecres.wholphin.ui.AspectRatio
import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.Cards
@ -26,8 +28,8 @@ internal fun naturalCardBaseDp(aspectRatio: AspectRatio): Int =
AspectRatio.TALL, AspectRatio.SQUARE, AspectRatio.FOUR_THREE -> Cards.HEIGHT_2X3_DP AspectRatio.TALL, AspectRatio.SQUARE, AspectRatio.FOUR_THREE -> Cards.HEIGHT_2X3_DP
} }
// The global Card slider, the per-row multiplier, and any preset-set absolute heightDp // The global Card size, the per-row multiplier, and any preset-set absolute heightDp
// all stack. Preset 148dp + global 150% + per-row 100% = 222dp. The base is the row's // all stack. Preset 148dp + global 116% + per-row 110% = ~189dp. The base is the row's
// preset if present, otherwise the natural height for its aspect. // preset if present, otherwise the natural height for its aspect.
internal fun resolveCardHeight( internal fun resolveCardHeight(
heightDp: Int, heightDp: Int,
@ -40,19 +42,9 @@ internal fun resolveCardHeight(
return base * globalCardSizePercent / 100 * cardSizeMultiplier / 100 return base * globalCardSizePercent / 100 * cardSizeMultiplier / 100
} }
// Scales density (and therefore both dp layouts and sp text) linearly by uiScalePercent. @Composable
// fontScale is left at the user's system value so Android's accessibility setting still @ReadOnlyComposable
// applies on top - scaling both would render text at factor^2 instead of factor. internal fun scaledSp(baseSp: Int): TextUnit = LocalInterfaceCustomization.current.textSizeLevel.scaledSp(baseSp)
internal fun scaledDensity(
base: Density,
uiScalePercent: Int,
): Density {
val factor = uiScalePercent / 100f
return Density(
density = base.density * factor,
fontScale = base.fontScale,
)
}
internal fun Int.withinBoundsOrDefault(preference: AppSliderPreference<*>): Int { internal fun Int.withinBoundsOrDefault(preference: AppSliderPreference<*>): Int {
val min = preference.min.toInt() val min = preference.min.toInt()

View file

@ -1,6 +1,5 @@
package com.github.damontecres.wholphin.ui.util package com.github.damontecres.wholphin.ui.util
import androidx.compose.ui.unit.Density
import com.github.damontecres.wholphin.data.model.HomeRowConfig import com.github.damontecres.wholphin.data.model.HomeRowConfig
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.preferences.AppPreference import com.github.damontecres.wholphin.preferences.AppPreference
@ -102,35 +101,6 @@ class InterfaceHelpersTest {
assertEquals(Cards.HEIGHT_2X3_DP, naturalCardBaseDp(AspectRatio.FOUR_THREE)) assertEquals(Cards.HEIGHT_2X3_DP, naturalCardBaseDp(AspectRatio.FOUR_THREE))
} }
@Test
fun `scaledDensity scales density linearly and leaves fontScale alone`() {
val base = Density(density = 2.0f, fontScale = 1.0f)
val scaled = scaledDensity(base, 150)
// density grows by 1.5 - so dp layouts scale 1.5x.
assertEquals(3.0f, scaled.density, 0.001f)
// fontScale stays put - sp text is (density * fontScale) so it also scales linearly.
// Scaling fontScale too would render text at 1.5^2 = 2.25x.
assertEquals(1.0f, scaled.fontScale, 0.001f)
}
@Test
fun `scaledDensity at 100 percent returns identity`() {
val base = Density(density = 2.0f, fontScale = 1.2f)
val scaled = scaledDensity(base, 100)
assertEquals(base.density, scaled.density, 0.001f)
assertEquals(base.fontScale, scaled.fontScale, 0.001f)
}
@Test
fun `scaledDensity preserves user accessibility fontScale across UI scale changes`() {
// A user with system fontScale 1.3x (accessibility setting) bumping UI scale to 75%
// should keep fontScale = 1.3 - the UI scale slider is independent of accessibility.
val base = Density(density = 2.0f, fontScale = 1.3f)
val scaled = scaledDensity(base, 75)
assertEquals(1.5f, scaled.density, 0.001f)
assertEquals(1.3f, scaled.fontScale, 0.001f)
}
@Test @Test
fun `InterfaceCustomization cardHeightDp at DEFAULT level matches natural base`() { fun `InterfaceCustomization cardHeightDp at DEFAULT level matches natural base`() {
val customization = InterfaceCustomization(prefs = AppPreferences.getDefaultInstance()) val customization = InterfaceCustomization(prefs = AppPreferences.getDefaultInstance())