From f039be0495b63f6976f9ad81f9e484ef3d4bf15f Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Thu, 28 May 2026 11:46:02 +0200 Subject: [PATCH] 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. --- .../damontecres/wholphin/MainActivity.kt | 1 + .../wholphin/WholphinDreamService.kt | 5 +- .../damontecres/wholphin/ui/theme/Theme.kt | 9 ++- .../damontecres/wholphin/ui/theme/Type.kt | 58 ++++++++++++++++++- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt index 80f86954..a4a4f869 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -232,6 +232,7 @@ class MainActivity : AppCompatActivity() { WholphinTheme( true, appThemeColors = appPreferences.interfacePreferences.appThemeColors, + textSizeLevel = appPreferences.interfacePreferences.textSizeLevel, ) { ProvideLocalClock { MainContent( diff --git a/app/src/main/java/com/github/damontecres/wholphin/WholphinDreamService.kt b/app/src/main/java/com/github/damontecres/wholphin/WholphinDreamService.kt index a281b101..aa898607 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/WholphinDreamService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/WholphinDreamService.kt @@ -100,7 +100,10 @@ class WholphinDreamService : debugLogging = false, enableCache = true, ) - WholphinTheme(appThemeColors = prefs.interfacePreferences.appThemeColors) { + WholphinTheme( + appThemeColors = prefs.interfacePreferences.appThemeColors, + textSizeLevel = prefs.interfacePreferences.textSizeLevel, + ) { ProvideLocalClock { val screensaverPrefs = prefs.interfacePreferences.screensaverPreference val currentItem by itemFlow.collectAsState(null) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Theme.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Theme.kt index 53c10bd2..3aa3711e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Theme.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Theme.kt @@ -3,8 +3,10 @@ package com.github.damontecres.wholphin.ui.theme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.remember import androidx.tv.material3.MaterialTheme 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.BoldBlueThemeColors import com.github.damontecres.wholphin.ui.theme.colors.GreenThemeColors @@ -30,6 +32,7 @@ fun getThemeColors(appThemeColors: AppThemeColors): ThemeColors = fun WholphinTheme( darkTheme: Boolean = true, appThemeColors: AppThemeColors = AppThemeColors.PURPLE, + textSizeLevel: DisplaySizeLevel = DisplaySizeLevel.DEFAULT, content: @Composable () -> Unit, ) { val themeColors = getThemeColors(appThemeColors) @@ -39,14 +42,16 @@ fun WholphinTheme( darkTheme -> themeColors.darkScheme else -> themeColors.lightScheme } + val tvTypography = remember(textSizeLevel) { AppTypography.scaledBy(textSizeLevel) } + val materialTypography = remember(tvTypography) { tvTypography.toMaterialTypography() } CompositionLocalProvider(LocalTheme provides appThemeColors) { androidx.compose.material3.MaterialTheme( colorScheme = if (darkTheme) themeColors.darkSchemeMaterial else themeColors.lightSchemeMaterial, - typography = androidx.compose.material3.Typography(), + typography = materialTypography, ) { MaterialTheme( colorScheme = colorScheme, - typography = AppTypography, + typography = tvTypography, content = content, ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Type.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Type.kt index baa5d047..46ce3074 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Type.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/theme/Type.kt @@ -1,5 +1,59 @@ 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, + ) +}