Font choice rework (#6670)

* Font choice rework

* Font choice rework - naming

* Font choice rework - fix default font selection
This commit is contained in:
SomeTroglodyte
2022-05-08 20:22:23 +02:00
committed by GitHub
parent 4b7edca7a8
commit 3ea9c6503b
6 changed files with 141 additions and 60 deletions

View File

@ -6,20 +6,42 @@ import android.graphics.Paint
import android.graphics.Typeface
import android.graphics.fonts.Font
import android.graphics.fonts.FontFamily
import android.graphics.fonts.FontStyle
import android.graphics.fonts.SystemFonts
import android.os.Build
import com.badlogic.gdx.graphics.Pixmap
import com.unciv.ui.utils.FontData
import com.unciv.ui.utils.FontFamilyData
import com.unciv.ui.utils.NativeFontImplementation
import java.util.*
import kotlin.math.abs
/**
* Created by tian on 2016/10/2.
*/
class NativeFontAndroid(private val size: Int, private val fontFamily: String) :
NativeFontImplementation {
class NativeFontAndroid(
private val size: Int,
private val fontFamily: String
) : NativeFontImplementation {
private val fontList =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) emptySet()
else SystemFonts.getAvailableFonts()
private val paint = Paint().apply {
typeface = if (fontFamily.isNotBlank() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val font = fontList.firstOrNull { it.file?.nameWithoutExtension == fontFamily }
// Helper within the VERSION_CODES.Q gate: Evaluate a Font's desirability (lower = better) for a given family.
fun Font.matchesFamily(family: String): Int {
val name = file?.nameWithoutExtension ?: return Int.MAX_VALUE
if (name == family) return 0
if (!name.startsWith("$family-")) return Int.MAX_VALUE
if (style.weight == FontStyle.FONT_WEIGHT_NORMAL && style.slant == FontStyle.FONT_SLANT_UPRIGHT) return 1
return 2 +
abs(style.weight - FontStyle.FONT_WEIGHT_NORMAL) / 100 +
abs(style.slant - FontStyle.FONT_SLANT_UPRIGHT)
}
val font = fontList.mapNotNull {
val distanceToRegular = it.matchesFamily(fontFamily)
if (distanceToRegular == Int.MAX_VALUE) null else it to distanceToRegular
}.minByOrNull { it.second }?.first
if (font != null) {
Typeface.CustomFallbackBuilder(FontFamily.Builder(font).build())
.setSystemFallback(fontFamily).build()
@ -32,10 +54,6 @@ class NativeFontAndroid(private val size: Int, private val fontFamily: String) :
setARGB(255, 255, 255, 255)
}
private val fontList: List<Font>
get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) emptyList()
else SystemFonts.getAvailableFonts().toList()
override fun getFontSize(): Int {
return size
}
@ -64,13 +82,35 @@ class NativeFontAndroid(private val size: Int, private val fontFamily: String) :
return pixmap
}
override fun getAvailableFont(): Collection<FontData> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
SystemFonts.getAvailableFonts().asSequence().mapNotNull {
it.file?.nameWithoutExtension
}.map { FontData(it) }.toSet()
} else {
listOf(FontData("sans-serif"), FontData("serif"), FontData("mono"))
override fun getAvailableFontFamilies(): Sequence<FontFamilyData> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
return sequenceOf(FontFamilyData("sans-serif"), FontFamilyData("serif"), FontFamilyData("mono"))
fun String.stripFromFirstDash(): String {
val dashPos = indexOf('-')
if (dashPos < 0) return this
return this.substring(0, dashPos)
}
// To get _all_ Languages a user has in their Android settings, we would need more help
// from the launcher: (Activity).resources.configuration.locales
val languageTag = Locale.getDefault().toLanguageTag() // e.g. he-IL, corresponds to the _first_ Language in Android settings
val supportedLocales = arrayOf(languageTag, "en-US")
val supportedLanguages = supportedLocales.map { it.take(2) }
return fontList.asSequence()
.mapNotNull {
if (it.file == null) return@mapNotNull null
val fontLocale = it.localeList.getFirstMatch(supportedLocales)
val fontScriptToLanguage = fontLocale?.script?.take(2)?.lowercase()
// The font localeList contains locales that have nothing to do with the system locales
// their language and country fields are empty - so **guess** that the first two letters
// of their Script (coming in at 4 chars) corresponds to the first two of the default Locale toLanguageTag:
if (!it.localeList.isEmpty && fontScriptToLanguage !in supportedLanguages)
return@mapNotNull null
// The API talks about FontFamily, but I see no methods to ask for the family of a Font instance.
// No displayName either. So, again, infer from the file name:
it.file!!.nameWithoutExtension.stripFromFirstDash()
}.distinct()
.map { FontFamilyData(it, it) }
}
}
}