Add settings for variable font axes in typography scheme
This commit is contained in:
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
@@ -29,6 +30,7 @@ import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FilterChipDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LeadingIconTab
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedIconToggleButton
|
||||
@@ -46,6 +48,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -72,6 +75,7 @@ import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||
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.component.preferences.SliderPreference
|
||||
import de.mm20.launcher2.ui.theme.typography.fontFamilyOf
|
||||
import de.mm20.launcher2.ui.theme.typography.typographyOf
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -745,24 +749,60 @@ private fun FontPreference(
|
||||
val fontManager = FontManager(context)
|
||||
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
var showFontSettings by remember { mutableStateOf(false) }
|
||||
|
||||
Preference(
|
||||
title = title,
|
||||
summary = getFontName(context, value),
|
||||
icon = {
|
||||
Text(
|
||||
text = preview.ExtraShort,
|
||||
style = TextStyle(
|
||||
fontFamily = remember(value) { fontFamilyOf(context, value) },
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontSize = 24.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
val fontVariantAxes = remember(value) {
|
||||
if (value is ThemeFontFamily.VariableFontFamily) {
|
||||
fontManager.getFontSettings(value)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.background(
|
||||
MaterialTheme.colorScheme.surface,
|
||||
MaterialTheme.shapes.extraSmall
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Preference(
|
||||
title = title,
|
||||
summary = getFontName(context, value),
|
||||
icon = {
|
||||
Text(
|
||||
text = preview.ExtraShort,
|
||||
style = TextStyle(
|
||||
fontFamily = remember(value) { fontFamilyOf(context, value) },
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontSize = 24.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
)
|
||||
},
|
||||
onClick = { showDialog = true },
|
||||
containerColor = Color.Transparent,
|
||||
)
|
||||
},
|
||||
onClick = { showDialog = true },
|
||||
)
|
||||
}
|
||||
if (fontVariantAxes.isNotEmpty()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.width(1.dp)
|
||||
.background(MaterialTheme.colorScheme.outlineVariant)
|
||||
)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
onClick = { showFontSettings = true }
|
||||
) {
|
||||
Icon(painterResource(R.drawable.tune_24px), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DismissableBottomSheet(
|
||||
expanded = showDialog,
|
||||
@@ -774,7 +814,8 @@ private fun FontPreference(
|
||||
start = 16.dp,
|
||||
top = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding(),
|
||||
bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues()
|
||||
.calculateBottomPadding(),
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
@@ -816,6 +857,59 @@ private fun FontPreference(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value is ThemeFontFamily.VariableFontFamily) {
|
||||
DismissableBottomSheet(
|
||||
expanded = showFontSettings,
|
||||
onDismissRequest = {
|
||||
showFontSettings = false
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp)
|
||||
.heightIn(min = 200.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = preview.TwoLines,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
fontFamily = fontFamilyOf(context, value)
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceCategory {
|
||||
for (axis in fontVariantAxes) {
|
||||
SliderPreference(
|
||||
title = axis.label,
|
||||
value = value.settings.get(axis.name) ?: axis.defaultValue,
|
||||
onValueChanged = {
|
||||
val map = value.settings.toMutableMap()
|
||||
map[axis.name] = it
|
||||
|
||||
if (value is ThemeFontFamily.LauncherDefault) {
|
||||
onValueChange(value.copy(settings = map))
|
||||
}
|
||||
},
|
||||
min = axis.range.start,
|
||||
max = axis.range.endInclusive,
|
||||
step = axis.step,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -860,7 +954,6 @@ private fun FontPickerCategory(
|
||||
private fun getFontName(context: Context, fontFamily: ThemeFontFamily?): String {
|
||||
return when (fontFamily) {
|
||||
is ThemeFontFamily.LauncherDefault -> "Google Sans Flex"
|
||||
is ThemeFontFamily.LauncherDefaultRound -> "Google Sans Flex (Rounded)"
|
||||
is ThemeFontFamily.DeviceHeadline -> context.getString(R.string.font_name_device_headline)
|
||||
is ThemeFontFamily.DeviceBody -> context.getString(R.string.font_name_device_body)
|
||||
is ThemeFontFamily.System -> fontFamily.name
|
||||
|
||||
@@ -13,7 +13,6 @@ 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.GoogleSansFlex
|
||||
import de.mm20.launcher2.ui.theme.typography.fontfamily.GoogleSansFlexRound
|
||||
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
|
||||
@@ -317,8 +316,7 @@ fun fontFamilyOf(
|
||||
fontFamily: ThemeFontFamily?
|
||||
): FontFamily {
|
||||
return when (fontFamily) {
|
||||
is ThemeFontFamily.LauncherDefault -> GoogleSansFlex
|
||||
is ThemeFontFamily.LauncherDefaultRound -> GoogleSansFlexRound
|
||||
is ThemeFontFamily.LauncherDefault -> GoogleSansFlex(fontFamily.settings)
|
||||
is ThemeFontFamily.DeviceHeadline -> getDeviceHeadlineFontFamily(context)
|
||||
is ThemeFontFamily.DeviceBody -> getDeviceBodyFontFamily(context)
|
||||
is ThemeFontFamily.SansSerif -> FontFamily.SansSerif
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
package de.mm20.launcher2.ui.theme.typography.fontfamily
|
||||
|
||||
import androidx.compose.ui.text.font.FontVariation
|
||||
import de.mm20.launcher2.ui.R
|
||||
|
||||
|
||||
|
||||
val GoogleSansFlex = createVariableFontFamily(
|
||||
fun GoogleSansFlex(settings: Map<String, Float>) = createVariableFontFamily(
|
||||
R.font.google_sans_flex,
|
||||
FontVariation.roundness(0f)
|
||||
settings
|
||||
)
|
||||
|
||||
val GoogleSansFlexRound = createVariableFontFamily(
|
||||
R.font.google_sans_flex,
|
||||
FontVariation.roundness( 100f)
|
||||
)
|
||||
@@ -1,17 +1,35 @@
|
||||
package de.mm20.launcher2.ui.theme.typography.fontfamily
|
||||
|
||||
import androidx.annotation.FontRes
|
||||
import androidx.collection.LruCache
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontVariation
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import java.io.File
|
||||
|
||||
internal fun FontVariation.roundness(roundness: Float): FontVariation.Setting {
|
||||
return Setting("ROND", roundness)
|
||||
|
||||
private val fontCache = LruCache<String, FontFamily>(20)
|
||||
|
||||
internal fun createVariableFontFamily(@FontRes resId: Int, settings: Map<String, Float>): FontFamily {
|
||||
val cacheKey = "0.$resId/${settings.entries.joinToString(separator = ",") { "${it.key}=${it.value}" }}"
|
||||
|
||||
val cacheHit = fontCache.get(cacheKey)
|
||||
if (cacheHit != null) {
|
||||
return cacheHit
|
||||
}
|
||||
|
||||
val family = createVariableFontFamily(
|
||||
resId, *settings.map { FontVariation.Setting(it.key, it.value) }.toTypedArray()
|
||||
)
|
||||
|
||||
fontCache.put(cacheKey, family)
|
||||
|
||||
return family
|
||||
}
|
||||
|
||||
internal fun createVariableFontFamily(@FontRes resId: Int, vararg settings: FontVariation.Setting): FontFamily {
|
||||
private fun createVariableFontFamily(@FontRes resId: Int, vararg settings: FontVariation.Setting): FontFamily {
|
||||
val fonts = mutableListOf<Font>()
|
||||
for (weight in 100..900 step 100) {
|
||||
fonts += Font(
|
||||
@@ -38,4 +56,4 @@ internal fun createVariableFontFamily(@FontRes resId: Int, vararg settings: Font
|
||||
)
|
||||
}
|
||||
return FontFamily(fonts)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package de.mm20.launcher2.themes.typography
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.fonts.SystemFonts
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.graphics.TypefaceCompat
|
||||
|
||||
data class FontList(
|
||||
val builtIn: List<FontFamily>,
|
||||
@@ -14,6 +9,14 @@ data class FontList(
|
||||
val system: List<FontFamily>,
|
||||
)
|
||||
|
||||
data class VariableFontAxis(
|
||||
val name: String,
|
||||
val label: String,
|
||||
val range: ClosedFloatingPointRange<Float>,
|
||||
val step: Float,
|
||||
val defaultValue: Float,
|
||||
)
|
||||
|
||||
class FontManager(
|
||||
private val context: Context
|
||||
) {
|
||||
@@ -24,12 +27,12 @@ class FontManager(
|
||||
.getIdentifier("config_bodyFontFamily", "string", "android")
|
||||
|
||||
val deviceHeadlineExists = deviceHeadlineResId != 0 &&
|
||||
context.resources.getString(deviceHeadlineResId).isNotBlank()
|
||||
context.resources.getString(deviceHeadlineResId).isNotBlank()
|
||||
|
||||
val deviceBodyExists = deviceBodyResId != 0 &&
|
||||
context.resources.getString(deviceBodyResId).isNotBlank()
|
||||
context.resources.getString(deviceBodyResId).isNotBlank()
|
||||
return FontList(
|
||||
builtIn = listOf(FontFamily.LauncherDefault, FontFamily.LauncherDefaultRound),
|
||||
builtIn = listOf(FontFamily.LauncherDefault()),
|
||||
generic = listOf(
|
||||
FontFamily.SansSerif,
|
||||
FontFamily.Serif,
|
||||
@@ -42,4 +45,16 @@ class FontManager(
|
||||
system = emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
fun getFontSettings(font: FontFamily.VariableFontFamily): List<VariableFontAxis> {
|
||||
if (font is FontFamily.LauncherDefault) {
|
||||
return listOf(
|
||||
VariableFontAxis("wdth", "Width", 25f..151f, 1f, 100f),
|
||||
VariableFontAxis("ROND", "Roundness", 0f..100f, 1f, 0f),
|
||||
VariableFontAxis("GRAD", "Grade", 0f..100f, 1f, 0f),
|
||||
)
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -181,13 +181,18 @@ data class TextStyle<out W : FontWeight?>(
|
||||
|
||||
@Serializable
|
||||
sealed interface FontFamily {
|
||||
@Serializable
|
||||
@SerialName("launcher_default")
|
||||
data object LauncherDefault : FontFamily
|
||||
|
||||
sealed interface VariableFontFamily {
|
||||
val settings: Map<String, Float>
|
||||
get() = emptyMap()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("launcher_default_round")
|
||||
data object LauncherDefaultRound : FontFamily
|
||||
@SerialName("launcher_default")
|
||||
data class LauncherDefault(
|
||||
override val settings: Map<String, Float> = emptyMap()
|
||||
) : FontFamily, VariableFontFamily
|
||||
|
||||
|
||||
@Serializable
|
||||
@SerialName("device_headline")
|
||||
|
||||
@@ -78,8 +78,8 @@ class TypographyRepository(
|
||||
builtIn = true,
|
||||
name = "Google Sans",
|
||||
fonts = mapOf(
|
||||
"brand" to FontFamily.LauncherDefault,
|
||||
"plain" to FontFamily.LauncherDefault,
|
||||
"brand" to FontFamily.LauncherDefault(),
|
||||
"plain" to FontFamily.LauncherDefault(),
|
||||
),
|
||||
styles = DefaultTextStyles,
|
||||
emphasizedStyles = DefaultEmphasizedTextStyles,
|
||||
@@ -91,8 +91,8 @@ class TypographyRepository(
|
||||
builtIn = true,
|
||||
name = "Google Sans (Rounded)",
|
||||
fonts = mapOf(
|
||||
"brand" to FontFamily.LauncherDefaultRound,
|
||||
"plain" to FontFamily.LauncherDefaultRound,
|
||||
"brand" to FontFamily.LauncherDefault(mapOf("ROND" to 100f)),
|
||||
"plain" to FontFamily.LauncherDefault(mapOf("ROND" to 100f)),
|
||||
),
|
||||
styles = DefaultTextStyles,
|
||||
emphasizedStyles = DefaultEmphasizedTextStyles,
|
||||
|
||||
Reference in New Issue
Block a user