(feat) custom typography schemes
This commit is contained in:
@@ -84,6 +84,10 @@ import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemeSettingsRo
|
||||
import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemeSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemesSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.typography.TypographiesSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.typography.TypographiesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.typography.TypographySettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.typography.TypographySettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterHelpSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.weather.WeatherIntegrationSettingsScreen
|
||||
@@ -218,6 +222,14 @@ class SettingsActivity : BaseActivity() {
|
||||
?: return@composable
|
||||
TransparencySchemeSettingsScreen(UUID.fromString(route.id))
|
||||
}
|
||||
composable<TypographiesSettingsRoute> {
|
||||
TypographiesSettingsScreen()
|
||||
}
|
||||
composable<TypographySettingsRoute> {
|
||||
val route: TypographySettingsRoute = it.toRoute()
|
||||
?: return@composable
|
||||
TypographySettingsScreen(UUID.fromString(route.id))
|
||||
}
|
||||
composable("settings/appearance/cards") {
|
||||
CardsSettingsScreen()
|
||||
}
|
||||
|
||||
+7
-17
@@ -29,14 +29,15 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.value
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemesSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.typography.TypographiesSettingsRoute
|
||||
import de.mm20.launcher2.ui.theme.getTypography
|
||||
|
||||
@Composable
|
||||
fun AppearanceSettingsScreen() {
|
||||
val viewModel: AppearanceSettingsScreenVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val colorThemeName by viewModel.colorThemeName.collectAsStateWithLifecycle(null)
|
||||
val typographyThemeName by viewModel.typographyThemeName.collectAsStateWithLifecycle(null)
|
||||
val shapeThemeName by viewModel.shapeThemeName.collectAsStateWithLifecycle(null)
|
||||
val transparencyThemeName by viewModel.transparencyThemeName.collectAsStateWithLifecycle(null)
|
||||
val compatModeColors by viewModel.compatModeColors.collectAsState()
|
||||
@@ -77,22 +78,11 @@ fun AppearanceSettingsScreen() {
|
||||
},
|
||||
icon = Icons.Rounded.Palette,
|
||||
)
|
||||
val font by viewModel.font.collectAsState()
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_font),
|
||||
items = listOf(
|
||||
"Outfit" to Font.Outfit,
|
||||
stringResource(R.string.preference_font_system) to Font.System,
|
||||
),
|
||||
value = font,
|
||||
onValueChanged = {
|
||||
if (it != null) viewModel.setFont(it)
|
||||
},
|
||||
itemLabel = {
|
||||
val typography = remember(it.value) {
|
||||
getTypography(context, it.value)
|
||||
}
|
||||
Text(it.first, style = typography.titleMedium)
|
||||
Preference(
|
||||
title = stringResource(id = R.string.preference_screen_typography),
|
||||
summary = typographyThemeName,
|
||||
onClick = {
|
||||
navController?.navigate(TypographiesSettingsRoute)
|
||||
},
|
||||
icon = Icons.Rounded.TextFields,
|
||||
)
|
||||
|
||||
+7
@@ -39,6 +39,13 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val typographyThemeName = uiSettings.typographyId.flatMapLatest {
|
||||
themeRepository.typographies.getOrDefault(it)
|
||||
}.map {
|
||||
it.name
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val transparencyThemeName = uiSettings.transparenciesId.flatMapLatest {
|
||||
themeRepository.transparencies.getOrDefault(it)
|
||||
}.map {
|
||||
|
||||
+9
-7
@@ -65,6 +65,7 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import de.mm20.launcher2.ui.settings.transparencies.checkerboard
|
||||
import de.mm20.launcher2.ui.settings.typography.PreviewTexts
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.darkColorSchemeOf
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.lightColorSchemeOf
|
||||
import de.mm20.launcher2.ui.theme.shapes.shapesOf
|
||||
@@ -233,6 +234,7 @@ private fun ThemePreview(
|
||||
darkMode: Boolean,
|
||||
onDarkModeChanged: (Boolean) -> Unit,
|
||||
) {
|
||||
val previewTexts = PreviewTexts()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -291,17 +293,17 @@ private fun ThemePreview(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
"Title",
|
||||
previewTexts.Medium1,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
"Subtitle",
|
||||
previewTexts.Medium2,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
Text(
|
||||
"Body",
|
||||
previewTexts.TwoLines,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
@@ -316,7 +318,7 @@ private fun ThemePreview(
|
||||
) {
|
||||
FilterChip(
|
||||
selected = true,
|
||||
label = { Text("Chip") },
|
||||
label = { Text(previewTexts.Short1) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Rounded.Star, null,
|
||||
@@ -327,7 +329,7 @@ private fun ThemePreview(
|
||||
)
|
||||
FilterChip(
|
||||
selected = false,
|
||||
label = { Text("Chip") },
|
||||
label = { Text(previewTexts.Short2) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Rounded.Star, null,
|
||||
@@ -348,8 +350,8 @@ private fun ThemePreview(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
|
||||
) {
|
||||
Button(onClick = {}) { Text("Button") }
|
||||
OutlinedButton(onClick = {}) { Text("Button") }
|
||||
Button(onClick = {}) { Text(previewTexts.Medium1) }
|
||||
OutlinedButton(onClick = {}) { Text(previewTexts.Medium2) }
|
||||
}
|
||||
}
|
||||
Box(
|
||||
|
||||
+54
-23
@@ -18,7 +18,6 @@ import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
import androidx.compose.material.icons.rounded.RadioButtonChecked
|
||||
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
|
||||
import androidx.compose.material.icons.rounded.Share
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
@@ -61,6 +60,8 @@ fun ColorSchemesSettingsScreen() {
|
||||
|
||||
var deleteColors by remember { mutableStateOf<Colors?>(null) }
|
||||
|
||||
val (builtin, user) = themes.partition { it.builtIn }
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_screen_colors),
|
||||
topBarActions = {
|
||||
@@ -71,7 +72,7 @@ fun ColorSchemesSettingsScreen() {
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in themes) {
|
||||
for (theme in builtin) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
@@ -90,18 +91,6 @@ fun ColorSchemesSettingsScreen() {
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (!theme.builtIn) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/colors/${theme.id}")
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
@@ -112,14 +101,56 @@ fun ColorSchemesSettingsScreen() {
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
if (!theme.builtIn) {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTheme(theme)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user.isNotEmpty()) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in user) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
title = theme.name,
|
||||
controls = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ColorSchemePreview(theme)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Share, null)
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.menu_share)) },
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
viewModel.exportTheme(context, theme)
|
||||
navController?.navigate("settings/appearance/colors/${theme.id}")
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
@@ -135,12 +166,12 @@ fun ColorSchemesSettingsScreen() {
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTheme(theme)
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTheme(theme)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+60
-19
@@ -54,6 +54,8 @@ fun ShapeSchemesSettingsScreen() {
|
||||
|
||||
var deleteShapes by remember { mutableStateOf<Shapes?>(null) }
|
||||
|
||||
val (builtin, user) = themes.partition { it.builtIn }
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_screen_shapes),
|
||||
topBarActions = {
|
||||
@@ -64,7 +66,7 @@ fun ShapeSchemesSettingsScreen() {
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in themes) {
|
||||
for (theme in builtin) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
@@ -83,18 +85,6 @@ fun ShapeSchemesSettingsScreen() {
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (!theme.builtIn) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/shapes/${theme.id}")
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
@@ -105,7 +95,58 @@ fun ShapeSchemesSettingsScreen() {
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
if (!theme.builtIn) {
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectShapes(theme)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user.isNotEmpty()) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in user) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
title = theme.name,
|
||||
controls = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ShapesPreview(theme)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
navController?.navigate("settings/appearance/shapes/${theme.id}")
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Delete, null)
|
||||
@@ -118,12 +159,12 @@ fun ShapeSchemesSettingsScreen() {
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectShapes(theme)
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectShapes(theme)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+68
-24
@@ -20,7 +20,6 @@ import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
@@ -33,8 +32,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -65,6 +62,8 @@ fun TransparencySchemesSettingsScreen() {
|
||||
|
||||
var deleteTransparencies by remember { mutableStateOf<Transparencies?>(null) }
|
||||
|
||||
val (builtin, user) = themes.partition { it.builtIn }
|
||||
|
||||
val wallpaperColors = wallpaperColorsAsState().value
|
||||
|
||||
PreferenceScreen(
|
||||
@@ -77,7 +76,7 @@ fun TransparencySchemesSettingsScreen() {
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in themes) {
|
||||
for (theme in builtin) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
@@ -96,7 +95,48 @@ fun TransparencySchemesSettingsScreen() {
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (!theme.builtIn) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTransparencies(theme)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user.isNotEmpty()) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in user) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
title = theme.name,
|
||||
controls = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TransparenciesPreview(wallpaperColors, theme)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
@@ -109,18 +149,16 @@ fun TransparencySchemesSettingsScreen() {
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
if (!theme.builtIn) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Delete, null)
|
||||
@@ -133,12 +171,12 @@ fun TransparencySchemesSettingsScreen() {
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTransparencies(theme)
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTransparencies(theme)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,13 +231,19 @@ private fun TransparenciesPreview(wallpaperColors: WallpaperColors, theme: Trans
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surfaceContainer.copy(alpha = transparencies.background), MaterialTheme.shapes.extraSmall)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceContainer.copy(alpha = transparencies.background),
|
||||
MaterialTheme.shapes.extraSmall
|
||||
)
|
||||
.height(40.dp)
|
||||
.width(56.dp)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface.copy(alpha = transparencies.surface), MaterialTheme.shapes.extraSmall)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surface.copy(alpha = transparencies.surface),
|
||||
MaterialTheme.shapes.extraSmall
|
||||
)
|
||||
.height(24.dp)
|
||||
.width(48.dp)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package de.mm20.launcher2.ui.settings.typography
|
||||
|
||||
import android.icu.util.ULocale
|
||||
|
||||
/**
|
||||
* Preview texts to demonstrate typography settings in the users dominant script.
|
||||
* Preview texts should be similar in length, and they should be language-neutral, and not use
|
||||
* letters that are used only in some languages.
|
||||
*/
|
||||
interface PreviewTexts {
|
||||
/**
|
||||
* Preview for the font. Should be a character that is representative for a font.
|
||||
*/
|
||||
val ExtraShort: String
|
||||
|
||||
/**
|
||||
* A short, 3-letter preview.
|
||||
*/
|
||||
val Short1: String
|
||||
|
||||
/**
|
||||
* An alternative to [Short1], also 3 letters.
|
||||
*/
|
||||
val Short2: String
|
||||
/**
|
||||
* A medium-length preview, 5-6 letters.
|
||||
*/
|
||||
val Medium1: String
|
||||
/**
|
||||
* An alternative to [Medium1], also 5-6 letters.
|
||||
*/
|
||||
val Medium2: String
|
||||
/**
|
||||
* A longer preview, to demonstrate line height.
|
||||
* Should contain letters and numbers, and a newline.
|
||||
*/
|
||||
val TwoLines: String
|
||||
|
||||
companion object {
|
||||
operator fun invoke(): PreviewTexts {
|
||||
val script = ULocale.addLikelySubtags(ULocale.getDefault()).script
|
||||
return forScript(script)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate [PreviewTexts] implementation based on the script.
|
||||
* Defaults to [LatinPreviewTexts] if the script is not recognized.
|
||||
* @param script the ISO-15924 script code
|
||||
*/
|
||||
fun forScript(script: String): PreviewTexts {
|
||||
return when (script) {
|
||||
"Latn" -> LatinPreviewTexts
|
||||
"Cyrl" -> CyrillicPreviewTexts
|
||||
"Grek" -> GreekPreviewTexts
|
||||
else -> LatinPreviewTexts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object LatinPreviewTexts: PreviewTexts {
|
||||
override val ExtraShort: String = "Aa"
|
||||
override val Short1: String = "Abc"
|
||||
override val Short2: String = "Deg"
|
||||
override val Medium1: String = "Abcdeg"
|
||||
override val Medium2: String = "Hilmno"
|
||||
override val TwoLines: String = "Abcdeghilm Nop\nRst 123456890"
|
||||
}
|
||||
|
||||
object CyrillicPreviewTexts: PreviewTexts {
|
||||
override val ExtraShort: String = "Аа"
|
||||
override val Short1: String = "Абв"
|
||||
override val Short2: String = "Где"
|
||||
override val Medium1: String = "Абвгде"
|
||||
override val Medium2: String = "Жзиклм"
|
||||
override val TwoLines: String = "Абвгдежзик Лмн\nОпр 1234567890"
|
||||
}
|
||||
|
||||
object GreekPreviewTexts: PreviewTexts {
|
||||
override val ExtraShort: String = "Αα"
|
||||
override val Short1: String = "Αβγ"
|
||||
override val Short2: String = "Δεζ"
|
||||
override val Medium1: String = "Αβγδεζ"
|
||||
override val Medium2: String = "Ηθικλμ"
|
||||
override val TwoLines: String = "Αβγδεζηθ Ικλμ\nΝξο 1234567890"
|
||||
}
|
||||
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
package de.mm20.launcher2.ui.settings.typography
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.ContentCopy
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
import androidx.compose.material.icons.rounded.RadioButtonChecked
|
||||
import androidx.compose.material.icons.rounded.RadioButtonUnchecked
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
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
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.themes.typography.Typography
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
import de.mm20.launcher2.ui.theme.typography.typographyOf
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data object TypographiesSettingsRoute
|
||||
|
||||
@Composable
|
||||
fun TypographiesSettingsScreen() {
|
||||
val viewModel: TypographySettingsScreenVM = viewModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val selectedTheme by viewModel.selectedTypography.collectAsStateWithLifecycle(null)
|
||||
val themes by viewModel.typography.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
var deleteTypography by remember { mutableStateOf<Typography?>(null) }
|
||||
|
||||
val (builtin, user) = themes.partition { it.builtIn }
|
||||
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_screen_typography),
|
||||
topBarActions = {
|
||||
IconButton(onClick = { viewModel.createNew(context) }) {
|
||||
Icon(Icons.Rounded.Add, null)
|
||||
}
|
||||
},
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in builtin) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
val typo = typographyOf(theme)
|
||||
Preference(
|
||||
icon = {
|
||||
Icon(
|
||||
if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = theme.name,
|
||||
maxLines = 1,
|
||||
style = typo.titleMedium
|
||||
)
|
||||
},
|
||||
controls = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TypographyPreview(typo)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTypography(theme)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user.isNotEmpty()) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (theme in user) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
val typo = typographyOf(theme)
|
||||
Preference(
|
||||
icon = {
|
||||
Icon(
|
||||
if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = theme.name,
|
||||
maxLines = 1,
|
||||
style = typo.titleMedium
|
||||
)
|
||||
},
|
||||
controls = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TypographyPreview(typo)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Edit, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.edit)) },
|
||||
onClick = {
|
||||
navController?.navigate(
|
||||
TypographySettingsRoute(theme.id.toString())
|
||||
)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.ContentCopy, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
onClick = {
|
||||
viewModel.duplicate(theme)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(Icons.Rounded.Delete, null)
|
||||
},
|
||||
text = { Text(stringResource(R.string.menu_delete)) },
|
||||
onClick = {
|
||||
deleteTypography = theme
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.selectTypography(theme)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleteTypography != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { deleteTypography = null },
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.confirmation_delete_transparencies_scheme,
|
||||
deleteTypography!!.name
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.delete(deleteTypography!!)
|
||||
deleteTypography = null
|
||||
}
|
||||
) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { deleteTypography = null }
|
||||
) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TypographyPreview(typography: androidx.compose.material3.Typography) {
|
||||
val previewTexts = PreviewTexts()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = previewTexts.Short1,
|
||||
style = typography.titleSmall,
|
||||
)
|
||||
Text(
|
||||
text = previewTexts.Short2,
|
||||
style = typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
+1098
File diff suppressed because it is too large
Load Diff
+50
@@ -0,0 +1,50 @@
|
||||
package de.mm20.launcher2.ui.settings.typography
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.themes.typography.Typography
|
||||
import de.mm20.launcher2.ui.R
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.util.UUID
|
||||
|
||||
class TypographySettingsScreenVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val selectedTypography = uiSettings.typographyId
|
||||
val typography: Flow<List<Typography>> = themeRepository.typographies.getAll()
|
||||
|
||||
fun getTypography(id: UUID): Flow<Typography?> {
|
||||
return themeRepository.typographies.get(id)
|
||||
}
|
||||
|
||||
fun updateTypography(typography: Typography) {
|
||||
themeRepository.typographies.update(typography)
|
||||
}
|
||||
|
||||
fun selectTypography(typography: Typography) {
|
||||
uiSettings.setTypographyId(typography.id)
|
||||
}
|
||||
|
||||
fun duplicate(typography: Typography) {
|
||||
themeRepository.typographies.create(typography.copy(id = UUID.randomUUID()))
|
||||
}
|
||||
|
||||
fun delete(typography: Typography) {
|
||||
themeRepository.typographies.delete(typography)
|
||||
}
|
||||
|
||||
fun createNew(context: Context) {
|
||||
themeRepository.typographies.create(
|
||||
Typography(
|
||||
id = UUID.randomUUID(),
|
||||
name = context.getString(R.string.new_theme_name)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme
|
||||
import de.mm20.launcher2.ui.theme.transparency.transparencySchemeOf
|
||||
import de.mm20.launcher2.ui.theme.typography.DefaultTypography
|
||||
import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography
|
||||
import de.mm20.launcher2.ui.theme.typography.typographyOf
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import org.koin.compose.koinInject
|
||||
import de.mm20.launcher2.preferences.ColorScheme as ColorSchemePref
|
||||
@@ -24,8 +25,6 @@ import de.mm20.launcher2.preferences.ColorScheme as ColorSchemePref
|
||||
fun LauncherTheme(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val uiSettings: UiSettings = koinInject()
|
||||
val themeRepository: ThemeRepository = koinInject()
|
||||
|
||||
@@ -41,6 +40,12 @@ fun LauncherTheme(
|
||||
}
|
||||
}.collectAsState(null)
|
||||
|
||||
val themeTypography by remember {
|
||||
uiSettings.typographyId.flatMapLatest {
|
||||
themeRepository.typographies.getOrDefault(it)
|
||||
}
|
||||
}.collectAsState(null)
|
||||
|
||||
val themeTransparencies by remember {
|
||||
uiSettings.transparenciesId.flatMapLatest {
|
||||
themeRepository.transparencies.getOrDefault(it)
|
||||
@@ -53,7 +58,7 @@ fun LauncherTheme(
|
||||
val darkTheme =
|
||||
colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme()
|
||||
|
||||
if (themeColors == null || themeShapes == null || themeTransparencies == null) {
|
||||
if (themeColors == null || themeShapes == null || themeTransparencies == null || themeTypography == null) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,6 +69,7 @@ fun LauncherTheme(
|
||||
}
|
||||
|
||||
val shapes = shapesOf(themeShapes!!)
|
||||
val typography = typographyOf(themeTypography!!)
|
||||
|
||||
val transparencyScheme = transparencySchemeOf(themeTransparencies!!)
|
||||
|
||||
@@ -72,10 +78,6 @@ fun LauncherTheme(
|
||||
Font.Outfit
|
||||
)
|
||||
|
||||
val typography = remember(font) {
|
||||
getTypography(context, font)
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalDarkTheme provides darkTheme,
|
||||
LocalTransparencyScheme provides transparencyScheme,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package de.mm20.launcher2.ui.theme.colorscheme
|
||||
|
||||
import android.R
|
||||
import android.os.Build
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -89,11 +88,11 @@ fun systemCorePalette(): CorePalette<Int> {
|
||||
if (Build.VERSION.SDK_INT >= 31 && !compatModeColors) {
|
||||
val context = LocalContext.current
|
||||
return CorePalette(
|
||||
primary = ContextCompat.getColor(context, R.color.system_accent1_500),
|
||||
secondary = ContextCompat.getColor(context, R.color.system_accent2_500),
|
||||
tertiary = ContextCompat.getColor(context, R.color.system_accent3_500),
|
||||
neutral = ContextCompat.getColor(context, R.color.system_neutral1_500),
|
||||
neutralVariant = ContextCompat.getColor(context, R.color.system_neutral2_500),
|
||||
primary = ContextCompat.getColor(context, android.R.color.system_accent1_500),
|
||||
secondary = ContextCompat.getColor(context, android.R.color.system_accent2_500),
|
||||
tertiary = ContextCompat.getColor(context, android.R.color.system_accent3_500),
|
||||
neutral = ContextCompat.getColor(context, android.R.color.system_neutral1_500),
|
||||
neutralVariant = ContextCompat.getColor(context, android.R.color.system_neutral2_500),
|
||||
error = 0xFFB3261E.toInt(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ fun makeTypography(
|
||||
headlineSmallEmphasized = baseTypography.headlineSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||
titleLarge = baseTypography.titleLarge.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||
titleLargeEmphasized = baseTypography.titleLargeEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||
titleMedium = baseTypography.titleMedium.copy(fontFamily = headlineFamily),
|
||||
titleMedium = baseTypography.titleMedium.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||
titleMediumEmphasized = baseTypography.titleMediumEmphasized.copy(fontFamily = headlineFamily),
|
||||
titleSmall = baseTypography.titleSmall.copy(fontFamily = headlineFamily, fontWeight = FontWeight.SemiBold),
|
||||
titleSmallEmphasized = baseTypography.titleSmallEmphasized.copy(fontFamily = headlineFamily, fontWeight = FontWeight.Bold),
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
package de.mm20.launcher2.ui.theme.typography
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.em
|
||||
import androidx.compose.ui.unit.sp
|
||||
import de.mm20.launcher2.themes.typography.DefaultEmphasizedTextStyles
|
||||
import de.mm20.launcher2.themes.typography.DefaultTextStyles
|
||||
import de.mm20.launcher2.ui.theme.typography.fontfamily.Outfit
|
||||
import de.mm20.launcher2.ui.theme.typography.fontfamily.getDeviceBodyFontFamily
|
||||
import de.mm20.launcher2.ui.theme.typography.fontfamily.getDeviceHeadlineFontFamily
|
||||
import de.mm20.launcher2.themes.typography.FontFamily as ThemeFontFamily
|
||||
import de.mm20.launcher2.themes.typography.FontWeight as ThemeFontWeight
|
||||
import de.mm20.launcher2.themes.typography.TextStyle as ThemeTextStyle
|
||||
import de.mm20.launcher2.themes.typography.Typography as ThemeTypography
|
||||
|
||||
@Composable
|
||||
fun typographyOf(typography: ThemeTypography): Typography {
|
||||
val context = LocalContext.current
|
||||
return remember(context, typography) {
|
||||
val base = Typography()
|
||||
|
||||
val fonts = getFontFamilies(context, typography.fonts)
|
||||
|
||||
base.copy(
|
||||
displayLarge = textStyleOf(
|
||||
typography.styles.displayLarge,
|
||||
DefaultTextStyles.displayLarge!!,
|
||||
base.displayLarge,
|
||||
fonts,
|
||||
),
|
||||
displayLargeEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.displayLarge,
|
||||
typography.styles.displayLarge,
|
||||
DefaultEmphasizedTextStyles.displayLarge!!,
|
||||
DefaultTextStyles.displayLarge!!,
|
||||
base.displayLargeEmphasized,
|
||||
fonts,
|
||||
),
|
||||
displayMedium = textStyleOf(
|
||||
typography.styles.displayMedium,
|
||||
DefaultTextStyles.displayMedium!!,
|
||||
base.displayMedium,
|
||||
fonts,
|
||||
),
|
||||
displayMediumEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.displayMedium,
|
||||
typography.styles.displayMedium,
|
||||
DefaultEmphasizedTextStyles.displayMedium!!,
|
||||
DefaultTextStyles.displayMedium!!,
|
||||
base.displayMediumEmphasized,
|
||||
fonts,
|
||||
),
|
||||
displaySmall = textStyleOf(
|
||||
typography.styles.displaySmall,
|
||||
DefaultTextStyles.displaySmall!!,
|
||||
base.displaySmall,
|
||||
fonts,
|
||||
),
|
||||
displaySmallEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.displaySmall,
|
||||
typography.styles.displaySmall,
|
||||
DefaultEmphasizedTextStyles.displaySmall!!,
|
||||
DefaultTextStyles.displaySmall!!,
|
||||
base.displaySmallEmphasized,
|
||||
fonts,
|
||||
),
|
||||
headlineLarge = textStyleOf(
|
||||
typography.styles.headlineLarge,
|
||||
DefaultTextStyles.headlineLarge!!,
|
||||
base.headlineLarge,
|
||||
fonts,
|
||||
),
|
||||
headlineLargeEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.headlineLarge,
|
||||
typography.styles.headlineLarge,
|
||||
DefaultEmphasizedTextStyles.headlineLarge!!,
|
||||
DefaultTextStyles.headlineLarge!!,
|
||||
base.headlineLargeEmphasized,
|
||||
fonts,
|
||||
),
|
||||
headlineMedium = textStyleOf(
|
||||
typography.styles.headlineMedium,
|
||||
DefaultTextStyles.headlineMedium!!,
|
||||
base.headlineMedium,
|
||||
fonts,
|
||||
),
|
||||
headlineMediumEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.headlineMedium,
|
||||
typography.styles.headlineMedium,
|
||||
DefaultEmphasizedTextStyles.headlineMedium!!,
|
||||
DefaultTextStyles.headlineMedium!!,
|
||||
base.headlineMediumEmphasized,
|
||||
fonts,
|
||||
),
|
||||
headlineSmall = textStyleOf(
|
||||
typography.styles.headlineSmall,
|
||||
DefaultTextStyles.headlineSmall!!,
|
||||
base.headlineSmall,
|
||||
fonts,
|
||||
),
|
||||
headlineSmallEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.headlineSmall,
|
||||
typography.styles.headlineSmall,
|
||||
DefaultEmphasizedTextStyles.headlineSmall!!,
|
||||
DefaultTextStyles.headlineSmall!!,
|
||||
base.headlineSmallEmphasized,
|
||||
fonts,
|
||||
),
|
||||
titleLarge = textStyleOf(
|
||||
typography.styles.titleLarge,
|
||||
DefaultTextStyles.titleLarge!!,
|
||||
base.titleLarge,
|
||||
fonts,
|
||||
),
|
||||
titleLargeEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.titleLarge,
|
||||
typography.styles.titleLarge,
|
||||
DefaultEmphasizedTextStyles.titleLarge!!,
|
||||
DefaultTextStyles.titleLarge!!,
|
||||
base.titleLargeEmphasized,
|
||||
fonts,
|
||||
),
|
||||
titleMedium = textStyleOf(
|
||||
typography.styles.titleMedium,
|
||||
DefaultTextStyles.titleMedium!!,
|
||||
base.titleMedium,
|
||||
fonts,
|
||||
),
|
||||
titleMediumEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.titleMedium,
|
||||
typography.styles.titleMedium,
|
||||
DefaultEmphasizedTextStyles.titleMedium!!,
|
||||
DefaultTextStyles.titleMedium!!,
|
||||
base.titleMediumEmphasized,
|
||||
fonts,
|
||||
),
|
||||
titleSmall = textStyleOf(
|
||||
typography.styles.titleSmall,
|
||||
DefaultTextStyles.titleSmall!!,
|
||||
base.titleSmall,
|
||||
fonts,
|
||||
),
|
||||
titleSmallEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.titleSmall,
|
||||
typography.styles.titleSmall,
|
||||
DefaultEmphasizedTextStyles.titleSmall!!,
|
||||
DefaultTextStyles.titleSmall!!,
|
||||
base.titleSmallEmphasized,
|
||||
fonts,
|
||||
),
|
||||
bodyLarge = textStyleOf(
|
||||
typography.styles.bodyLarge,
|
||||
DefaultTextStyles.bodyLarge!!,
|
||||
base.bodyLarge,
|
||||
fonts,
|
||||
),
|
||||
bodyLargeEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.bodyLarge,
|
||||
typography.styles.bodyLarge,
|
||||
DefaultEmphasizedTextStyles.bodyLarge!!,
|
||||
DefaultTextStyles.bodyLarge!!,
|
||||
base.bodyLargeEmphasized,
|
||||
fonts,
|
||||
),
|
||||
bodyMedium = textStyleOf(
|
||||
typography.styles.bodyMedium,
|
||||
DefaultTextStyles.bodyMedium!!,
|
||||
base.bodyMedium,
|
||||
fonts,
|
||||
),
|
||||
bodyMediumEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.bodyMedium,
|
||||
typography.styles.bodyMedium,
|
||||
DefaultEmphasizedTextStyles.bodyMedium!!,
|
||||
DefaultTextStyles.bodyMedium!!,
|
||||
base.bodyMediumEmphasized,
|
||||
fonts,
|
||||
),
|
||||
bodySmall = textStyleOf(
|
||||
typography.styles.bodySmall,
|
||||
DefaultTextStyles.bodySmall!!,
|
||||
base.bodySmall,
|
||||
fonts,
|
||||
),
|
||||
bodySmallEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.bodySmall,
|
||||
typography.styles.bodySmall,
|
||||
DefaultEmphasizedTextStyles.bodySmall!!,
|
||||
DefaultTextStyles.bodySmall!!,
|
||||
base.bodySmallEmphasized,
|
||||
fonts,
|
||||
),
|
||||
labelLarge = textStyleOf(
|
||||
typography.styles.labelLarge,
|
||||
DefaultTextStyles.labelLarge!!,
|
||||
base.labelLarge,
|
||||
fonts,
|
||||
),
|
||||
labelLargeEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.labelLarge,
|
||||
typography.styles.labelLarge,
|
||||
DefaultEmphasizedTextStyles.labelLarge!!,
|
||||
DefaultTextStyles.labelLarge!!,
|
||||
base.labelLargeEmphasized,
|
||||
fonts,
|
||||
),
|
||||
labelMedium = textStyleOf(
|
||||
typography.styles.labelMedium,
|
||||
DefaultTextStyles.labelMedium!!,
|
||||
base.labelMedium,
|
||||
fonts,
|
||||
),
|
||||
labelMediumEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.labelMedium,
|
||||
typography.styles.labelMedium,
|
||||
DefaultEmphasizedTextStyles.labelMedium!!,
|
||||
DefaultTextStyles.labelMedium!!,
|
||||
base.labelMediumEmphasized,
|
||||
fonts,
|
||||
),
|
||||
labelSmall = textStyleOf(
|
||||
typography.styles.labelSmall,
|
||||
DefaultTextStyles.labelSmall!!,
|
||||
base.labelSmall,
|
||||
fonts,
|
||||
),
|
||||
labelSmallEmphasized = emphasizedTextStyleOf(
|
||||
typography.emphasizedStyles.labelSmall,
|
||||
typography.styles.labelSmall,
|
||||
DefaultEmphasizedTextStyles.labelSmall!!,
|
||||
DefaultTextStyles.labelSmall!!,
|
||||
base.labelSmallEmphasized,
|
||||
fonts,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun textStyleOf(
|
||||
style: ThemeTextStyle<ThemeFontWeight.Absolute?>?,
|
||||
fallback: ThemeTextStyle<ThemeFontWeight.Absolute?>,
|
||||
base: TextStyle,
|
||||
fonts: Map<String, FontFamily?>,
|
||||
): TextStyle {
|
||||
return base.copy(
|
||||
fontFamily = (style?.fontFamily ?: fallback.fontFamily)?.let { fonts[it] }
|
||||
?: base.fontFamily,
|
||||
fontWeight = (style?.fontWeight?.weight
|
||||
?: fallback.fontWeight?.weight)?.let { FontWeight(it) } ?: base.fontWeight,
|
||||
fontSize = (style?.fontSize ?: fallback.fontSize)?.sp ?: base.fontSize,
|
||||
lineHeight = (style?.lineHeight ?: fallback.lineHeight)?.em ?: base.lineHeight,
|
||||
letterSpacing = (style?.letterSpacing ?: fallback.letterSpacing)?.em ?: base.letterSpacing,
|
||||
)
|
||||
}
|
||||
|
||||
private fun emphasizedTextStyleOf(
|
||||
style: ThemeTextStyle<ThemeFontWeight?>?,
|
||||
parent: ThemeTextStyle<ThemeFontWeight.Absolute?>?,
|
||||
fallback: ThemeTextStyle<ThemeFontWeight?>,
|
||||
fallbackParent: ThemeTextStyle<ThemeFontWeight.Absolute?>,
|
||||
base: TextStyle,
|
||||
fonts: Map<String, FontFamily?>,
|
||||
): TextStyle {
|
||||
val weight: ThemeFontWeight? = style?.fontWeight ?: fallback.fontWeight
|
||||
val parentWeight = parent?.fontWeight ?: fallbackParent.fontWeight
|
||||
|
||||
val fontWeight = when (weight) {
|
||||
is ThemeFontWeight.Absolute -> FontWeight(weight.weight)
|
||||
is ThemeFontWeight.Relative if (parentWeight != null) -> {
|
||||
FontWeight(parentWeight.weight + weight.relativeWeight)
|
||||
}
|
||||
|
||||
else -> base.fontWeight
|
||||
}
|
||||
|
||||
return base.copy(
|
||||
fontFamily = (style?.fontFamily
|
||||
?: parent?.fontFamily
|
||||
?: fallback.fontFamily
|
||||
?: fallbackParent.fontFamily)
|
||||
?.let { fonts[it] }
|
||||
?: base.fontFamily,
|
||||
fontWeight = fontWeight,
|
||||
fontSize = (style?.fontSize ?: parent?.fontSize ?: fallback.fontSize
|
||||
?: fallbackParent.fontSize)?.sp ?: base.fontSize,
|
||||
lineHeight = (style?.lineHeight ?: parent?.lineHeight ?: fallback.lineHeight
|
||||
?: fallbackParent.lineHeight)?.em
|
||||
?: base.lineHeight,
|
||||
letterSpacing = (style?.letterSpacing ?: parent?.letterSpacing
|
||||
?: fallback.letterSpacing ?: fallbackParent.letterSpacing)?.em ?: base.letterSpacing,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFontFamilies(
|
||||
context: Context,
|
||||
fonts: Map<String, ThemeFontFamily?>
|
||||
): Map<String, FontFamily?> {
|
||||
val distinct = fonts.values.distinct()
|
||||
|
||||
val map: Map<ThemeFontFamily?, FontFamily> = distinct.associateWith {
|
||||
fontFamilyOf(context, it)
|
||||
}
|
||||
|
||||
return fonts.keys.associateWith { map[fonts[it]] }
|
||||
}
|
||||
|
||||
fun fontFamilyOf(
|
||||
context: Context,
|
||||
fontFamily: ThemeFontFamily?
|
||||
): FontFamily {
|
||||
return when (fontFamily) {
|
||||
is ThemeFontFamily.LauncherDefault -> Outfit
|
||||
is ThemeFontFamily.DeviceHeadline -> getDeviceHeadlineFontFamily(context)
|
||||
is ThemeFontFamily.DeviceBody -> getDeviceBodyFontFamily(context)
|
||||
is ThemeFontFamily.SansSerif -> FontFamily.SansSerif
|
||||
is ThemeFontFamily.Serif -> FontFamily.Serif
|
||||
is ThemeFontFamily.Monospace -> FontFamily.Monospace
|
||||
else -> FontFamily.Default
|
||||
}
|
||||
}
|
||||
+37
@@ -41,4 +41,41 @@ fun getDeviceHeadlineFontFamily(context: Context): FontFamily {
|
||||
|
||||
return FontFamily.SansSerif
|
||||
|
||||
}
|
||||
|
||||
fun getDeviceBodyFontFamily(context: Context): FontFamily {
|
||||
val configResId = context.resources
|
||||
.getIdentifier("config_bodyFontFamily", "string", "android")
|
||||
|
||||
if (configResId != 0) {
|
||||
val fontFamily = context.resources.getString(configResId)
|
||||
|
||||
if (fontFamily.isBlank()) return FontFamily.SansSerif
|
||||
|
||||
return try {
|
||||
FontFamily(
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Thin, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.ExtraLight, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Light, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Normal, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Medium, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.SemiBold, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Bold, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.ExtraBold, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Black, style = FontStyle.Normal),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Thin, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.ExtraLight, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Light, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Normal, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Medium, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.SemiBold, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Bold, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.ExtraBold, style = FontStyle.Italic),
|
||||
Font(DeviceFontFamilyName(fontFamily), weight = FontWeight.Black, style = FontStyle.Italic),
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
FontFamily.SansSerif
|
||||
}
|
||||
}
|
||||
return FontFamily.SansSerif
|
||||
}
|
||||
Reference in New Issue
Block a user