mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Custom desktop font (#6377)
* Custom desktop font * Add `getDesktopAllFonts` to setting custom desktop font. * Custom font. `desktopFontFamily` change to `fontFamily`. Add GameSettings.getSettingsForPlatformLaunchers(). * Add `Custom font` setting UI. * Add `Custom font` on Android. * `Default Font` use translations. * format * remove open fun. Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
@ -562,6 +562,8 @@ Enable portrait orientation = Hochkant-Orientierung zulassen
|
||||
Generate translation files = Erstelle Übersetzungsdateien
|
||||
Translation files are generated successfully. = Die Übersetzungsdateien wurden erfolgreich erstellt.
|
||||
Please note that translations are a community-based work in progress and are INCOMPLETE! The percentage shown is how much of the language is translated in-game. If you want to help translating the game into your language, click here. = Bitte beachte, daß die Übersetzungen eine andauernde Leistung einer Gemeinschaft von Freiwilligen sind und damit oft unvollständig. Die angezeigte Prozentzahl bedeutet den Anteil übersetzter Texte im gesamten Spiel. Wenn Du helfen willst, die Übersetzungen zu verbessern - dies ist ein Link zur Anleitung.
|
||||
Font family = Schriftart
|
||||
You need to restart the game for this change to take effect. = Diese Änderung wird erst beim nächsten Start des Spiels wirksam.
|
||||
|
||||
# Notifications
|
||||
|
||||
|
@ -567,6 +567,9 @@ Enable portrait orientation = 启用竖屏
|
||||
Generate translation files = 生成翻译文件
|
||||
Translation files are generated successfully. = 翻译文件生成成功。
|
||||
Please note that translations are a community-based work in progress and are INCOMPLETE! The percentage shown is how much of the language is translated in-game. If you want to help translating the game into your language, click here. = 请注意,翻译是一项基于社区的正在进行的工作,并且是【不完整的】!显示的百分比是语言在游戏中的翻译量。如果您想帮助将游戏翻译成您的语言,请单击此处。
|
||||
Font family = 字体
|
||||
Default Font = 默认字体
|
||||
You need to restart the game for this change to take effect. = 您需要重新启动游戏才能使此更改生效。
|
||||
|
||||
# Notifications
|
||||
|
||||
|
@ -567,6 +567,9 @@ Enable portrait orientation =
|
||||
Generate translation files =
|
||||
Translation files are generated successfully. =
|
||||
Please note that translations are a community-based work in progress and are INCOMPLETE! The percentage shown is how much of the language is translated in-game. If you want to help translating the game into your language, click here. =
|
||||
Font family =
|
||||
Default Font =
|
||||
You need to restart the game for this change to take effect. =
|
||||
|
||||
# Notifications
|
||||
|
||||
|
@ -11,6 +11,7 @@ import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.UncivGameParameters
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import java.io.File
|
||||
|
||||
@ -34,12 +35,15 @@ open class AndroidLauncher : AndroidApplication() {
|
||||
val config = AndroidApplicationConfiguration().apply {
|
||||
useImmersiveMode = true;
|
||||
}
|
||||
|
||||
val fontFamily = GameSettings.getSettingsForPlatformLaunchers(filesDir.path).fontFamily
|
||||
|
||||
val androidParameters = UncivGameParameters(
|
||||
version = BuildConfig.VERSION_NAME,
|
||||
crashReportSysInfo = CrashReportSysInfoAndroid,
|
||||
fontImplementation = NativeFontAndroid(Fonts.ORIGINAL_FONT_SIZE.toInt()),
|
||||
customSaveLocationHelper = customSaveLocationHelper,
|
||||
limitOrientationsHelper = limitOrientationsHelper
|
||||
version = BuildConfig.VERSION_NAME,
|
||||
crashReportSysInfo = CrashReportSysInfoAndroid,
|
||||
fontImplementation = NativeFontAndroid(Fonts.ORIGINAL_FONT_SIZE.toInt(), fontFamily),
|
||||
customSaveLocationHelper = customSaveLocationHelper,
|
||||
limitOrientationsHelper = limitOrientationsHelper
|
||||
)
|
||||
|
||||
game = UncivGame(androidParameters)
|
||||
|
@ -3,21 +3,44 @@ package com.unciv.app
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.fonts.Font
|
||||
import android.graphics.fonts.FontFamily
|
||||
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.NativeFontImplementation
|
||||
|
||||
/**
|
||||
* Created by tian on 2016/10/2.
|
||||
*/
|
||||
class NativeFontAndroid(val size: Int) : NativeFontImplementation {
|
||||
class NativeFontAndroid(private val size: Int, private val fontFamily: String) :
|
||||
NativeFontImplementation {
|
||||
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 }
|
||||
if (font != null) {
|
||||
Typeface.CustomFallbackBuilder(FontFamily.Builder(font).build())
|
||||
.setSystemFallback(fontFamily).build()
|
||||
} else Typeface.create(fontFamily, Typeface.NORMAL)
|
||||
} else Typeface.create(fontFamily, Typeface.NORMAL)
|
||||
|
||||
isAntiAlias = true
|
||||
textSize = size.toFloat()
|
||||
strokeWidth = 0f
|
||||
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
|
||||
}
|
||||
|
||||
override fun getCharPixmap(char: Char): Pixmap {
|
||||
val paint = Paint()
|
||||
paint.isAntiAlias = true
|
||||
paint.textSize = size.toFloat()
|
||||
val metric = paint.fontMetrics
|
||||
var width = paint.measureText(char.toString()).toInt()
|
||||
var height = (metric.descent - metric.ascent).toInt()
|
||||
@ -27,8 +50,6 @@ class NativeFontAndroid(val size: Int) : NativeFontImplementation {
|
||||
}
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
paint.strokeWidth = 0f
|
||||
paint.setARGB(255, 255, 255, 255)
|
||||
canvas.drawText(char.toString(), 0f, -metric.ascent, paint)
|
||||
val pixmap = Pixmap(width, height, Pixmap.Format.RGBA8888)
|
||||
val data = IntArray(width * height)
|
||||
@ -42,4 +63,14 @@ class NativeFontAndroid(val size: Int) : NativeFontImplementation {
|
||||
bitmap.recycle()
|
||||
return pixmap
|
||||
}
|
||||
|
||||
override fun getAvailableFont(): Collection<FontData> {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
SystemFonts.getAvailableFonts().mapNotNull {
|
||||
it.file?.nameWithoutExtension
|
||||
}.map { FontData(it) }.toSet()
|
||||
} else {
|
||||
listOf(FontData("sans-serif"), FontData("serif"), FontData("mono"))
|
||||
}
|
||||
}
|
||||
}
|
@ -2,12 +2,16 @@ package com.unciv.models.metadata
|
||||
|
||||
import com.badlogic.gdx.Application
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.unciv.JsonParser
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.io.path.Path
|
||||
|
||||
data class WindowState (val width: Int = 900, val height: Int = 600)
|
||||
|
||||
@ -65,6 +69,8 @@ class GameSettings {
|
||||
/** Saves the last successful new game's setup */
|
||||
var lastGameSetup: GameSetupInfo? = null
|
||||
|
||||
var fontFamily: String = Fonts.DEFAULT_FONT_FAMILY
|
||||
|
||||
init {
|
||||
// 26 = Android Oreo. Versions below may display permanent icon in notification bar.
|
||||
if (Gdx.app?.type == Application.ApplicationType.Android && Gdx.app.version < 26) {
|
||||
@ -104,6 +110,24 @@ class GameSettings {
|
||||
fun getCollatorFromLocale(): Collator {
|
||||
return Collator.getInstance(getCurrentLocale())
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Specialized function to access settings before Gdx is initialized.
|
||||
*
|
||||
* @param base Path to the directory where the file should be - if not set, the OS current directory is used (which is "/" on Android)
|
||||
*/
|
||||
fun getSettingsForPlatformLaunchers(base: String = ""): GameSettings {
|
||||
// FileHandle is Gdx, but the class and JsonParser are not dependent on app initialization
|
||||
// If fact, at this point Gdx.app or Gdx.files are null but this still works.
|
||||
val file = FileHandle(Path(base, GameSaver.settingsFileName).toString())
|
||||
return if (file.exists())
|
||||
JsonParser().getFromJson(
|
||||
GameSettings::class.java,
|
||||
file
|
||||
)
|
||||
else GameSettings().apply { isFreshlyCreated = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class LocaleCode(var language: String, var country: String) {
|
||||
|
@ -18,6 +18,23 @@ import com.unciv.models.stats.Stat
|
||||
interface NativeFontImplementation {
|
||||
fun getFontSize(): Int
|
||||
fun getCharPixmap(char: Char): Pixmap
|
||||
fun getAvailableFont(): Collection<FontData>
|
||||
}
|
||||
|
||||
// If save in `GameSettings` need use enName.
|
||||
// If show to user need use localName.
|
||||
// If save localName in `GameSettings` may generate garbled characters by encoding.
|
||||
data class FontData(val localName: String, val enName: String = localName) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return if (other is FontData) enName == other.enName
|
||||
else super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = localName.hashCode()
|
||||
result = 31 * result + enName.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// This class is loosely based on libgdx's FreeTypeBitmapFontData
|
||||
@ -128,8 +145,9 @@ object Fonts {
|
||||
* This has several advantages: It means we only render each character once (good for both runtime and RAM),
|
||||
* AND it means that our 'custom' emojis only need to be once size (50px) and they'll be rescaled for what's needed. */
|
||||
const val ORIGINAL_FONT_SIZE = 50f
|
||||
const val DEFAULT_FONT_FAMILY = ""
|
||||
|
||||
lateinit var font:BitmapFont
|
||||
lateinit var font: BitmapFont
|
||||
fun resetFont() {
|
||||
val fontData = NativeBitmapFontData(UncivGame.Current.fontImplementation!!)
|
||||
font = BitmapFont(fontData, fontData.regions, false)
|
||||
@ -137,6 +155,11 @@ object Fonts {
|
||||
font.data.setScale(Constants.defaultFontSize / ORIGINAL_FONT_SIZE)
|
||||
}
|
||||
|
||||
fun getAvailableFontFamilyNames(): Collection<FontData> {
|
||||
if (UncivGame.Current.fontImplementation == null) return emptyList()
|
||||
return UncivGame.Current.fontImplementation!!.getAvailableFont()
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a TextureRegion into a Pixmap.
|
||||
*
|
||||
@ -145,15 +168,15 @@ object Fonts {
|
||||
* @return New Pixmap with all the size and pixel data from this TextureRegion copied into it.
|
||||
*/
|
||||
// From https://stackoverflow.com/questions/29451787/libgdx-textureregion-to-pixmap
|
||||
fun extractPixmapFromTextureRegion(textureRegion:TextureRegion):Pixmap {
|
||||
fun extractPixmapFromTextureRegion(textureRegion: TextureRegion): Pixmap {
|
||||
val textureData = textureRegion.texture.textureData
|
||||
if (!textureData.isPrepared) {
|
||||
textureData.prepare()
|
||||
}
|
||||
val pixmap = Pixmap(
|
||||
textureRegion.regionWidth,
|
||||
textureRegion.regionHeight,
|
||||
textureData.format
|
||||
textureRegion.regionWidth,
|
||||
textureRegion.regionHeight,
|
||||
textureData.format
|
||||
)
|
||||
val textureDataPixmap = textureData.consumePixmap()
|
||||
pixmap.drawPixmap(
|
||||
|
@ -368,6 +368,8 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
||||
}
|
||||
}
|
||||
|
||||
addFontFamilySelect(Fonts.getAvailableFontFamilyNames())
|
||||
|
||||
addTranslationGeneration()
|
||||
|
||||
addSetUserId()
|
||||
@ -879,6 +881,34 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Table.addFontFamilySelect(fonts: Collection<FontData>) {
|
||||
if (fonts.isEmpty()) return
|
||||
|
||||
add("Font family".toLabel()).left().fillX()
|
||||
|
||||
val fontSelectBox = SelectBox<String>(skin)
|
||||
val fontsLocalName = GdxArray<String>().apply { add("Default Font".tr()) }
|
||||
val fontsEnName = GdxArray<String>().apply { add("") }
|
||||
for (font in fonts) {
|
||||
fontsLocalName.add(font.localName)
|
||||
fontsEnName.add(font.enName)
|
||||
}
|
||||
|
||||
val selectedIndex = fontsEnName.indexOf(settings.fontFamily).let { if (it == -1) 0 else it }
|
||||
|
||||
fontSelectBox.items = fontsLocalName
|
||||
fontSelectBox.selected = fontsLocalName[selectedIndex]
|
||||
|
||||
add(fontSelectBox).minWidth(selectBoxMinWidth).pad(10f).row()
|
||||
|
||||
fontSelectBox.onChange {
|
||||
settings.fontFamily = fontsEnName[fontSelectBox.selectedIndex]
|
||||
ToastPopup(
|
||||
"You need to restart the game for this change to take effect.", previousScreen
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Table.addTranslationGeneration() {
|
||||
if (Gdx.app.type == Application.ApplicationType.Desktop) {
|
||||
val generateTranslationsButton = "Generate translation files".toTextButton()
|
||||
|
@ -32,12 +32,10 @@ internal object DesktopLauncher {
|
||||
config.setWindowIcon("ExtraImages/Icon.png")
|
||||
config.setTitle("Unciv")
|
||||
config.setHdpiMode(HdpiMode.Logical)
|
||||
config.setWindowSizeLimits(120, 80, -1, -1);
|
||||
if (FileHandle(GameSaver.settingsFileName).exists()) {
|
||||
val settings = JsonParser().getFromJson(
|
||||
GameSettings::class.java,
|
||||
FileHandle(GameSaver.settingsFileName)
|
||||
)
|
||||
config.setWindowSizeLimits(120, 80, -1, -1)
|
||||
|
||||
val settings = GameSettings.getSettingsForPlatformLaunchers()
|
||||
if (!settings.isFreshlyCreated) {
|
||||
config.setWindowedMode(settings.windowState.width.coerceAtLeast(120), settings.windowState.height.coerceAtLeast(80))
|
||||
}
|
||||
|
||||
@ -50,7 +48,7 @@ internal object DesktopLauncher {
|
||||
val desktopParameters = UncivGameParameters(
|
||||
versionFromJar,
|
||||
cancelDiscordEvent = { discordTimer?.cancel() },
|
||||
fontImplementation = NativeFontDesktop(Fonts.ORIGINAL_FONT_SIZE.toInt()),
|
||||
fontImplementation = NativeFontDesktop(Fonts.ORIGINAL_FONT_SIZE.toInt(), settings.fontFamily),
|
||||
customSaveLocationHelper = CustomSaveLocationHelperDesktop()
|
||||
)
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
package com.unciv.app.desktop
|
||||
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.unciv.ui.utils.FontData
|
||||
import com.unciv.ui.utils.NativeFontImplementation
|
||||
import java.awt.*
|
||||
import java.awt.image.BufferedImage
|
||||
import java.util.*
|
||||
|
||||
|
||||
class NativeFontDesktop(private val size: Int) : NativeFontImplementation {
|
||||
class NativeFontDesktop(private val size: Int, private val fontFamily: String) :
|
||||
NativeFontImplementation {
|
||||
private val font by lazy {
|
||||
Font("", Font.PLAIN, size)
|
||||
Font(fontFamily, Font.PLAIN, size)
|
||||
}
|
||||
private val metric by lazy {
|
||||
val bi = BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
@ -47,4 +49,11 @@ class NativeFontDesktop(private val size: Int) : NativeFontImplementation {
|
||||
g.dispose()
|
||||
return pixmap
|
||||
}
|
||||
|
||||
override fun getAvailableFont(): Collection<FontData> {
|
||||
val allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().allFonts.map {
|
||||
FontData(it.fontName, it.getFamily(Locale.ENGLISH))
|
||||
}.toSet()
|
||||
return allFonts
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user