Scale TV typography by Text size level; mirror to Material theme

WholphinTheme takes a new textSizeLevel parameter (defaults to DEFAULT).
AppTypography stays the TV Material3 default; scaledBy(level) returns a
TvTypography with each role's fontSize and lineHeight multiplied by the
level's textPercent (identity at DEFAULT). toMaterialTypography() copies
each role's TextStyle into a compose.material3.Typography so the inner
compose.material3.MaterialTheme inside WholphinTheme is fed from the same
source. Both wraps now scale uniformly, and any compose.material3
component reading MaterialTheme.typography.X now matches the TV M3 size
for X.

MainActivity and WholphinDreamService pass the level from prefs. Preview
call sites in cards/playback fall through to DEFAULT and render identically
to before.
This commit is contained in:
Justin Visser 2026-05-28 11:46:02 +02:00
parent c9433fbf57
commit f039be0495
4 changed files with 68 additions and 5 deletions

View file

@ -232,6 +232,7 @@ class MainActivity : AppCompatActivity() {
WholphinTheme( WholphinTheme(
true, true,
appThemeColors = appPreferences.interfacePreferences.appThemeColors, appThemeColors = appPreferences.interfacePreferences.appThemeColors,
textSizeLevel = appPreferences.interfacePreferences.textSizeLevel,
) { ) {
ProvideLocalClock { ProvideLocalClock {
MainContent( MainContent(

View file

@ -100,7 +100,10 @@ class WholphinDreamService :
debugLogging = false, debugLogging = false,
enableCache = true, enableCache = true,
) )
WholphinTheme(appThemeColors = prefs.interfacePreferences.appThemeColors) { WholphinTheme(
appThemeColors = prefs.interfacePreferences.appThemeColors,
textSizeLevel = prefs.interfacePreferences.textSizeLevel,
) {
ProvideLocalClock { ProvideLocalClock {
val screensaverPrefs = prefs.interfacePreferences.screensaverPreference val screensaverPrefs = prefs.interfacePreferences.screensaverPreference
val currentItem by itemFlow.collectAsState(null) val currentItem by itemFlow.collectAsState(null)

View file

@ -3,8 +3,10 @@ package com.github.damontecres.wholphin.ui.theme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import com.github.damontecres.wholphin.preferences.AppThemeColors import com.github.damontecres.wholphin.preferences.AppThemeColors
import com.github.damontecres.wholphin.preferences.DisplaySizeLevel
import com.github.damontecres.wholphin.ui.theme.colors.BlueThemeColors import com.github.damontecres.wholphin.ui.theme.colors.BlueThemeColors
import com.github.damontecres.wholphin.ui.theme.colors.BoldBlueThemeColors import com.github.damontecres.wholphin.ui.theme.colors.BoldBlueThemeColors
import com.github.damontecres.wholphin.ui.theme.colors.GreenThemeColors import com.github.damontecres.wholphin.ui.theme.colors.GreenThemeColors
@ -30,6 +32,7 @@ fun getThemeColors(appThemeColors: AppThemeColors): ThemeColors =
fun WholphinTheme( fun WholphinTheme(
darkTheme: Boolean = true, darkTheme: Boolean = true,
appThemeColors: AppThemeColors = AppThemeColors.PURPLE, appThemeColors: AppThemeColors = AppThemeColors.PURPLE,
textSizeLevel: DisplaySizeLevel = DisplaySizeLevel.DEFAULT,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
val themeColors = getThemeColors(appThemeColors) val themeColors = getThemeColors(appThemeColors)
@ -39,14 +42,16 @@ fun WholphinTheme(
darkTheme -> themeColors.darkScheme darkTheme -> themeColors.darkScheme
else -> themeColors.lightScheme else -> themeColors.lightScheme
} }
val tvTypography = remember(textSizeLevel) { AppTypography.scaledBy(textSizeLevel) }
val materialTypography = remember(tvTypography) { tvTypography.toMaterialTypography() }
CompositionLocalProvider(LocalTheme provides appThemeColors) { CompositionLocalProvider(LocalTheme provides appThemeColors) {
androidx.compose.material3.MaterialTheme( androidx.compose.material3.MaterialTheme(
colorScheme = if (darkTheme) themeColors.darkSchemeMaterial else themeColors.lightSchemeMaterial, colorScheme = if (darkTheme) themeColors.darkSchemeMaterial else themeColors.lightSchemeMaterial,
typography = androidx.compose.material3.Typography(), typography = materialTypography,
) { ) {
MaterialTheme( MaterialTheme(
colorScheme = colorScheme, colorScheme = colorScheme,
typography = AppTypography, typography = tvTypography,
content = content, content = content,
) )
} }

View file

@ -1,5 +1,59 @@
package com.github.damontecres.wholphin.ui.theme package com.github.damontecres.wholphin.ui.theme
import androidx.tv.material3.Typography import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.isSpecified
import com.github.damontecres.wholphin.preferences.DisplaySizeLevel
import com.github.damontecres.wholphin.preferences.textPercent
import androidx.compose.material3.Typography as MaterialTypography
import androidx.tv.material3.Typography as TvTypography
val AppTypography = Typography() val AppTypography: TvTypography = TvTypography()
fun TvTypography.scaledBy(level: DisplaySizeLevel): TvTypography {
val scale = level.textPercent() / 100f
if (scale == 1f) return this
return TvTypography(
displayLarge = displayLarge.scaledBy(scale),
displayMedium = displayMedium.scaledBy(scale),
displaySmall = displaySmall.scaledBy(scale),
headlineLarge = headlineLarge.scaledBy(scale),
headlineMedium = headlineMedium.scaledBy(scale),
headlineSmall = headlineSmall.scaledBy(scale),
titleLarge = titleLarge.scaledBy(scale),
titleMedium = titleMedium.scaledBy(scale),
titleSmall = titleSmall.scaledBy(scale),
bodyLarge = bodyLarge.scaledBy(scale),
bodyMedium = bodyMedium.scaledBy(scale),
bodySmall = bodySmall.scaledBy(scale),
labelLarge = labelLarge.scaledBy(scale),
labelMedium = labelMedium.scaledBy(scale),
labelSmall = labelSmall.scaledBy(scale),
)
}
fun TvTypography.toMaterialTypography(): MaterialTypography =
MaterialTypography(
displayLarge = displayLarge,
displayMedium = displayMedium,
displaySmall = displaySmall,
headlineLarge = headlineLarge,
headlineMedium = headlineMedium,
headlineSmall = headlineSmall,
titleLarge = titleLarge,
titleMedium = titleMedium,
titleSmall = titleSmall,
bodyLarge = bodyLarge,
bodyMedium = bodyMedium,
bodySmall = bodySmall,
labelLarge = labelLarge,
labelMedium = labelMedium,
labelSmall = labelSmall,
)
private fun TextStyle.scaledBy(scale: Float): TextStyle {
if (scale == 1f) return this
return copy(
fontSize = if (fontSize.isSpecified) fontSize * scale else fontSize,
lineHeight = if (lineHeight.isSpecified) lineHeight * scale else lineHeight,
)
}