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.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
@@ -29,6 +30,7 @@ import androidx.compose.material3.FilterChip
|
|||||||
import androidx.compose.material3.FilterChipDefaults
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LeadingIconTab
|
import androidx.compose.material3.LeadingIconTab
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedIconToggleButton
|
import androidx.compose.material3.OutlinedIconToggleButton
|
||||||
@@ -46,6 +48,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
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.Preference
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
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.fontFamilyOf
|
||||||
import de.mm20.launcher2.ui.theme.typography.typographyOf
|
import de.mm20.launcher2.ui.theme.typography.typographyOf
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@@ -745,24 +749,60 @@ private fun FontPreference(
|
|||||||
val fontManager = FontManager(context)
|
val fontManager = FontManager(context)
|
||||||
|
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
var showFontSettings by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Preference(
|
val fontVariantAxes = remember(value) {
|
||||||
title = title,
|
if (value is ThemeFontFamily.VariableFontFamily) {
|
||||||
summary = getFontName(context, value),
|
fontManager.getFontSettings(value)
|
||||||
icon = {
|
} else {
|
||||||
Text(
|
emptyList()
|
||||||
text = preview.ExtraShort,
|
}
|
||||||
style = TextStyle(
|
}
|
||||||
fontFamily = remember(value) { fontFamilyOf(context, value) },
|
|
||||||
fontWeight = FontWeight.Normal,
|
Row(
|
||||||
color = MaterialTheme.colorScheme.primary,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
fontSize = 24.sp,
|
modifier = Modifier.background(
|
||||||
textAlign = TextAlign.Center,
|
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(
|
DismissableBottomSheet(
|
||||||
expanded = showDialog,
|
expanded = showDialog,
|
||||||
@@ -774,7 +814,8 @@ private fun FontPreference(
|
|||||||
start = 16.dp,
|
start = 16.dp,
|
||||||
top = 16.dp,
|
top = 16.dp,
|
||||||
end = 16.dp,
|
end = 16.dp,
|
||||||
bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding(),
|
bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues()
|
||||||
|
.calculateBottomPadding(),
|
||||||
),
|
),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
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
|
@Composable
|
||||||
@@ -860,7 +954,6 @@ private fun FontPickerCategory(
|
|||||||
private fun getFontName(context: Context, fontFamily: ThemeFontFamily?): String {
|
private fun getFontName(context: Context, fontFamily: ThemeFontFamily?): String {
|
||||||
return when (fontFamily) {
|
return when (fontFamily) {
|
||||||
is ThemeFontFamily.LauncherDefault -> "Google Sans Flex"
|
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.DeviceHeadline -> context.getString(R.string.font_name_device_headline)
|
||||||
is ThemeFontFamily.DeviceBody -> context.getString(R.string.font_name_device_body)
|
is ThemeFontFamily.DeviceBody -> context.getString(R.string.font_name_device_body)
|
||||||
is ThemeFontFamily.System -> fontFamily.name
|
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.DefaultEmphasizedTextStyles
|
||||||
import de.mm20.launcher2.themes.typography.DefaultTextStyles
|
import de.mm20.launcher2.themes.typography.DefaultTextStyles
|
||||||
import de.mm20.launcher2.ui.theme.typography.fontfamily.GoogleSansFlex
|
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.getDeviceBodyFontFamily
|
||||||
import de.mm20.launcher2.ui.theme.typography.fontfamily.getDeviceHeadlineFontFamily
|
import de.mm20.launcher2.ui.theme.typography.fontfamily.getDeviceHeadlineFontFamily
|
||||||
import de.mm20.launcher2.themes.typography.FontFamily as ThemeFontFamily
|
import de.mm20.launcher2.themes.typography.FontFamily as ThemeFontFamily
|
||||||
@@ -317,8 +316,7 @@ fun fontFamilyOf(
|
|||||||
fontFamily: ThemeFontFamily?
|
fontFamily: ThemeFontFamily?
|
||||||
): FontFamily {
|
): FontFamily {
|
||||||
return when (fontFamily) {
|
return when (fontFamily) {
|
||||||
is ThemeFontFamily.LauncherDefault -> GoogleSansFlex
|
is ThemeFontFamily.LauncherDefault -> GoogleSansFlex(fontFamily.settings)
|
||||||
is ThemeFontFamily.LauncherDefaultRound -> GoogleSansFlexRound
|
|
||||||
is ThemeFontFamily.DeviceHeadline -> getDeviceHeadlineFontFamily(context)
|
is ThemeFontFamily.DeviceHeadline -> getDeviceHeadlineFontFamily(context)
|
||||||
is ThemeFontFamily.DeviceBody -> getDeviceBodyFontFamily(context)
|
is ThemeFontFamily.DeviceBody -> getDeviceBodyFontFamily(context)
|
||||||
is ThemeFontFamily.SansSerif -> FontFamily.SansSerif
|
is ThemeFontFamily.SansSerif -> FontFamily.SansSerif
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
package de.mm20.launcher2.ui.theme.typography.fontfamily
|
package de.mm20.launcher2.ui.theme.typography.fontfamily
|
||||||
|
|
||||||
import androidx.compose.ui.text.font.FontVariation
|
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
|
|
||||||
|
fun GoogleSansFlex(settings: Map<String, Float>) = createVariableFontFamily(
|
||||||
|
|
||||||
val GoogleSansFlex = createVariableFontFamily(
|
|
||||||
R.font.google_sans_flex,
|
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
|
package de.mm20.launcher2.ui.theme.typography.fontfamily
|
||||||
|
|
||||||
import androidx.annotation.FontRes
|
import androidx.annotation.FontRes
|
||||||
|
import androidx.collection.LruCache
|
||||||
import androidx.compose.ui.text.font.Font
|
import androidx.compose.ui.text.font.Font
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontVariation
|
import androidx.compose.ui.text.font.FontVariation
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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>()
|
val fonts = mutableListOf<Font>()
|
||||||
for (weight in 100..900 step 100) {
|
for (weight in 100..900 step 100) {
|
||||||
fonts += Font(
|
fonts += Font(
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
package de.mm20.launcher2.themes.typography
|
package de.mm20.launcher2.themes.typography
|
||||||
|
|
||||||
import android.content.Context
|
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(
|
data class FontList(
|
||||||
val builtIn: List<FontFamily>,
|
val builtIn: List<FontFamily>,
|
||||||
@@ -14,6 +9,14 @@ data class FontList(
|
|||||||
val system: List<FontFamily>,
|
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(
|
class FontManager(
|
||||||
private val context: Context
|
private val context: Context
|
||||||
) {
|
) {
|
||||||
@@ -24,12 +27,12 @@ class FontManager(
|
|||||||
.getIdentifier("config_bodyFontFamily", "string", "android")
|
.getIdentifier("config_bodyFontFamily", "string", "android")
|
||||||
|
|
||||||
val deviceHeadlineExists = deviceHeadlineResId != 0 &&
|
val deviceHeadlineExists = deviceHeadlineResId != 0 &&
|
||||||
context.resources.getString(deviceHeadlineResId).isNotBlank()
|
context.resources.getString(deviceHeadlineResId).isNotBlank()
|
||||||
|
|
||||||
val deviceBodyExists = deviceBodyResId != 0 &&
|
val deviceBodyExists = deviceBodyResId != 0 &&
|
||||||
context.resources.getString(deviceBodyResId).isNotBlank()
|
context.resources.getString(deviceBodyResId).isNotBlank()
|
||||||
return FontList(
|
return FontList(
|
||||||
builtIn = listOf(FontFamily.LauncherDefault, FontFamily.LauncherDefaultRound),
|
builtIn = listOf(FontFamily.LauncherDefault()),
|
||||||
generic = listOf(
|
generic = listOf(
|
||||||
FontFamily.SansSerif,
|
FontFamily.SansSerif,
|
||||||
FontFamily.Serif,
|
FontFamily.Serif,
|
||||||
@@ -42,4 +45,16 @@ class FontManager(
|
|||||||
system = emptyList(),
|
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
|
@Serializable
|
||||||
sealed interface FontFamily {
|
sealed interface FontFamily {
|
||||||
@Serializable
|
|
||||||
@SerialName("launcher_default")
|
sealed interface VariableFontFamily {
|
||||||
data object LauncherDefault : FontFamily
|
val settings: Map<String, Float>
|
||||||
|
get() = emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("launcher_default_round")
|
@SerialName("launcher_default")
|
||||||
data object LauncherDefaultRound : FontFamily
|
data class LauncherDefault(
|
||||||
|
override val settings: Map<String, Float> = emptyMap()
|
||||||
|
) : FontFamily, VariableFontFamily
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("device_headline")
|
@SerialName("device_headline")
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ class TypographyRepository(
|
|||||||
builtIn = true,
|
builtIn = true,
|
||||||
name = "Google Sans",
|
name = "Google Sans",
|
||||||
fonts = mapOf(
|
fonts = mapOf(
|
||||||
"brand" to FontFamily.LauncherDefault,
|
"brand" to FontFamily.LauncherDefault(),
|
||||||
"plain" to FontFamily.LauncherDefault,
|
"plain" to FontFamily.LauncherDefault(),
|
||||||
),
|
),
|
||||||
styles = DefaultTextStyles,
|
styles = DefaultTextStyles,
|
||||||
emphasizedStyles = DefaultEmphasizedTextStyles,
|
emphasizedStyles = DefaultEmphasizedTextStyles,
|
||||||
@@ -91,8 +91,8 @@ class TypographyRepository(
|
|||||||
builtIn = true,
|
builtIn = true,
|
||||||
name = "Google Sans (Rounded)",
|
name = "Google Sans (Rounded)",
|
||||||
fonts = mapOf(
|
fonts = mapOf(
|
||||||
"brand" to FontFamily.LauncherDefaultRound,
|
"brand" to FontFamily.LauncherDefault(mapOf("ROND" to 100f)),
|
||||||
"plain" to FontFamily.LauncherDefaultRound,
|
"plain" to FontFamily.LauncherDefault(mapOf("ROND" to 100f)),
|
||||||
),
|
),
|
||||||
styles = DefaultTextStyles,
|
styles = DefaultTextStyles,
|
||||||
emphasizedStyles = DefaultEmphasizedTextStyles,
|
emphasizedStyles = DefaultEmphasizedTextStyles,
|
||||||
|
|||||||
Reference in New Issue
Block a user