字体阴影与斜体 #141

This commit is contained in:
HapeLee
2025-10-26 02:08:38 +08:00
parent 1a37c2a01f
commit b53b2cdf04
10 changed files with 361 additions and 149 deletions
@@ -32,6 +32,8 @@ import io.legato.kazusa.utils.putPrefInt
import io.legato.kazusa.utils.resizeAndRecycle
import splitties.init.appCtx
import java.io.File
import kotlin.String
import androidx.core.graphics.drawable.toDrawable
/**
* 阅读界面配置
@@ -58,6 +60,7 @@ object ReadBookConfig {
var bg: Drawable? = null
var bgMeanColor: Int = 0
val textColor: Int get() = durConfig.curTextColor()
val textShadowColor: Int get() = durConfig.curTextShadowColor()
init {
initConfigs()
@@ -246,6 +249,36 @@ object ReadBookConfig {
config.textBold = value
}
var textItalic: Boolean
get() = config.textItalic
set(value) {
config.textItalic = value
}
var textShadow: Boolean
get() = config.textShadow
set(value) {
config.textShadow = value
}
var shadowRadius: Float
get() = config.shadowRadius
set(value) {
config.shadowRadius = value
}
var shadowDx: Float
get() = config.shadowDx
set(value) {
config.shadowDx = value
}
var shadowDy: Float
get() = config.shadowDy
set(value) {
config.shadowDy = value
}
var textSize: Int
get() = config.textSize
set(value) {
@@ -509,6 +542,13 @@ object ReadBookConfig {
var textFont: String = "",//字体
var textBold: Int = 500,//是否粗体字 0:正常, 1:粗体, 2:细体
var textSize: Int = 20,//文字大小
var textItalic: Boolean = false,// 是否启用斜体
var textShadow: Boolean = false,// 是否启用阴影
var shadowRadius: Float = 16f,// 阴影模糊半径
var shadowDx: Float = 1f,// 阴影x偏移
var shadowDy: Float = 1f,// 阴影y偏移
private var shadowColor: String = "#3E3D3B",
private var shadowColorN: String = "#3E3D3B",
var letterSpacing: Float = 0.1f,//字间距
var lineSpacingExtra: Int = 12,//行间距
var paragraphSpacing: Int = 2,//段距
@@ -553,16 +593,37 @@ object ReadBookConfig {
@Transient
private var textColorInt = -1
@Transient
private var textColorSIntNight = -1
@Transient
private var textColorSInt = -1
@Transient
private var initColorInt = false
private fun initColorInt() {
textColorIntEInk = Color.parseColor(textColorEInk)
textColorIntNight = Color.parseColor(textColorNight)
textColorInt = Color.parseColor(textColor)
textColorIntEInk = textColorEInk.toColorInt()
textColorIntNight = textColorNight.toColorInt()
textColorInt = textColor.toColorInt()
textColorSIntNight = shadowColorN.toColorInt()
textColorSInt = shadowColor.toColorInt()
initColorInt = true
}
fun setCurShadColor(color: Int){
when {
AppConfig.isNightTheme -> {
shadowColorN = "#${color.hexString}"
textColorSIntNight = color
}
else -> {
shadowColor = "#${color.hexString}"
textColorSInt = color
}
}
}
fun setCurTextColor(color: Int) {
when {
AppConfig.isEInkMode -> {
@@ -593,6 +654,16 @@ object ReadBookConfig {
}
}
fun curTextShadowColor(): Int {
if (!initColorInt) {
initColorInt()
}
return when {
AppConfig.isNightTheme -> textColorSIntNight
else -> textColorSInt
}
}
fun setCurStatusIconDark(isDark: Boolean) {
when {
AppConfig.isEInkMode -> darkStatusIconEInk = isDark
@@ -661,18 +732,18 @@ object ReadBookConfig {
fun curBgDrawable(width: Int, height: Int): Drawable {
if (width == 0 || height == 0) {
val backgroundColor = MaterialColors.getColor(appCtx, com.google.android.material.R.attr.colorSurface, Color.WHITE)
return ColorDrawable(backgroundColor)
return backgroundColor.toDrawable()
}
var bgDrawable: Drawable? = null
val resources = appCtx.resources
try {
bgDrawable = when (curBgType()) {
0 -> ColorDrawable(Color.parseColor(curBgStr()))
0 -> curBgStr().toColorInt().toDrawable()
1 -> {
val path = "bg" + File.separator + curBgStr()
val bitmap = BitmapUtils.decodeAssetsBitmap(appCtx, path, width, height)
BitmapDrawable(resources, bitmap?.resizeAndRecycle(width, height))
bitmap?.resizeAndRecycle(width, height)?.toDrawable(resources)
}
else -> {
val path = curBgStr().let {
@@ -680,7 +751,7 @@ object ReadBookConfig {
else FileUtils.getPath(appCtx.externalFiles, "bg", curBgStr())
}
val bitmap = BitmapUtils.decodeBitmap(path, width, height)
BitmapDrawable(resources, bitmap?.resizeAndRecycle(width, height))
bitmap?.resizeAndRecycle(width, height)?.toDrawable(resources)
}
}
} catch (e: OutOfMemoryError) {
@@ -691,7 +762,7 @@ object ReadBookConfig {
// fallback 使用 MD3 的 colorSurface 作为背景色
val fallbackColor = MaterialColors.getColor(appCtx, com.google.android.material.R.attr.colorSurface, Color.WHITE)
return bgDrawable ?: ColorDrawable(fallbackColor)
return bgDrawable ?: fallbackColor.toDrawable()
}
}
}
@@ -76,6 +76,7 @@ import io.legato.kazusa.ui.book.info.BookInfoActivity
import io.legato.kazusa.ui.book.read.config.AutoReadDialog
import io.legato.kazusa.ui.book.read.config.BgTextConfigDialog.Companion.BG_COLOR
import io.legato.kazusa.ui.book.read.config.BgTextConfigDialog.Companion.TEXT_COLOR
import io.legato.kazusa.ui.book.read.config.FontSelectDialog.Companion.S_COLOR
import io.legato.kazusa.ui.book.read.config.ReadAloudDialog
import io.legato.kazusa.ui.book.read.config.ReadStyleDialog
import io.legato.kazusa.ui.book.read.config.TipConfigDialog.Companion.TIP_COLOR
@@ -1504,6 +1505,10 @@ class ReadBookActivity : BaseReadBookActivity(),
*/
override fun onColorSelected(dialogId: Int, color: Int) = ReadBookConfig.durConfig.run {
when (dialogId) {
S_COLOR -> {
setCurShadColor(color)
postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
}
TEXT_COLOR -> {
setCurTextColor(color)
postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
@@ -1,17 +1,19 @@
package io.legato.kazusa.ui.book.read.config
//import io.legado.app.lib.theme.primaryColor
import android.content.Context
import android.graphics.Typeface
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.appcompat.widget.Toolbar
import io.legato.kazusa.help.config.ReadBookConfig
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.jaredrummler.android.colorpicker.ColorPickerDialog
import io.legato.kazusa.R
import io.legato.kazusa.base.BaseBottomSheetDialogFragment
import io.legato.kazusa.base.adapter.ItemViewHolder
@@ -22,7 +24,6 @@ import io.legato.kazusa.constant.PreferKey
import io.legato.kazusa.databinding.DialogFontSelectBinding
import io.legato.kazusa.databinding.ItemFontBinding
import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.help.config.ReadBookConfig
import io.legato.kazusa.lib.dialogs.SelectItem
import io.legato.kazusa.lib.dialogs.alert
import io.legato.kazusa.lib.permission.Permissions
@@ -54,6 +55,10 @@ import java.net.URLDecoder
*/
class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_select),
Toolbar.OnMenuItemClickListener {
companion object {
const val S_COLOR = 123
}
private val fontRegex = Regex("(?i).*\\.[ot]tf")
private val binding by viewBinding(DialogFontSelectBinding::bind)
private val adapter by lazy {
@@ -118,21 +123,19 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
((it - 50) / 100f).toString()
}
dsbLineSize.valueFormat = { ((it - 10) / 10f).toString() }
binding.dsbShadowRadius.valueFormat = { "${it.toInt()} px" }
binding.dsbShadowDx.valueFormat = { "${it.toInt()} px" }
binding.dsbShadowDy.valueFormat = { "${it.toInt()} px" }
binding.bgTextIndent.apply {
val currentIndex = ReadBookConfig.paragraphIndent.length.coerceAtMost(childCount - 1)
val defaultButtonId = getChildAt(currentIndex).id
check(defaultButtonId)
addOnButtonCheckedListener { group, checkedId, isChecked ->
if (isChecked) {
val checkedIndex = (0 until group.childCount)
.firstOrNull { group.getChildAt(it).id == checkedId }
?: return@addOnButtonCheckedListener
ReadBookConfig.paragraphIndent = " ".repeat(checkedIndex)
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.textIndentDropdown.apply {
val items = listOf("0", "1", "2", "3", "4")
val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, items)
setAdapter(adapter)
val currentIndex = ReadBookConfig.paragraphIndent.length.coerceIn(0, items.lastIndex)
setText(items[currentIndex], false)
setOnItemClickListener { _, _, position, _ ->
ReadBookConfig.paragraphIndent = " ".repeat(position)
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
}
@@ -166,6 +169,9 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 9, 6))
}
}
binding.btnTextItalic.isChecked = ReadBookConfig.textItalic
binding.btnTextShadow.isChecked = ReadBookConfig.textShadow
}
private fun initViewEvent() = binding.run {
@@ -192,6 +198,35 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
}
}
}
binding.btnTextItalic.addOnCheckedChangeListener { _, isChecked ->
ReadBookConfig.textItalic = isChecked
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.btnTextShadow.addOnCheckedChangeListener { _, isChecked ->
ReadBookConfig.textShadow = isChecked
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.dsbShadowRadius.onChanged = {
ReadBookConfig.shadowRadius = it.toFloat()
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.dsbShadowDx.onChanged = {
ReadBookConfig.shadowDx = it.toFloat()
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.dsbShadowDy.onChanged = {
ReadBookConfig.shadowDy = it.toFloat()
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.btnShadowColor.setOnClickListener {
ColorPickerDialog.newBuilder()
.setColor(ReadBookConfig.config.curTextShadowColor())
.setShowAlphaSlider(false)
.setDialogType(ColorPickerDialog.TYPE_CUSTOM)
.setDialogId(S_COLOR)
.show(requireActivity())
//postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
}
private fun upView() = binding.run {
@@ -199,6 +234,9 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
dsbTextLetterSpacing.progress = (it.letterSpacing * 100).toInt() + 50
dsbLineSize.progress = it.lineSpacingExtra
dsbParagraphSpacing.value = it.paragraphSpacing.toFloat()
binding.dsbShadowRadius.progress = it.shadowRadius.toInt()
binding.dsbShadowDx.progress = it.shadowDx.toInt()
binding.dsbShadowDy.progress = it.shadowDy.toInt()
}
}
@@ -1,5 +1,7 @@
package io.legato.kazusa.ui.book.read.page.provider
import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.Paint.FontMetrics
import android.graphics.RectF
import android.graphics.Typeface
@@ -38,6 +40,7 @@ import splitties.init.appCtx
import java.io.File
import java.util.LinkedList
import java.util.Locale
import androidx.core.graphics.toColorInt
/**
* 解析内容生成章节和页面
@@ -910,6 +913,7 @@ object ChapterProvider {
} ?: Typeface.DEFAULT
}
@SuppressLint("UseKtx")
private fun getPaints(typeface: Typeface?): Pair<TextPaint, TextPaint> {
val bold = Typeface.create(typeface, Typeface.BOLD)
val normal = Typeface.create(typeface, Typeface.NORMAL)
@@ -952,6 +956,17 @@ object ChapterProvider {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && AppConfig.optimizeRender) {
tPaint.isLinearText = true
}
if (ReadBookConfig.textItalic) {
tPaint.textSkewX = -0.25f
}
if (ReadBookConfig.textShadow) {
tPaint.setShadowLayer(
ReadBookConfig.shadowRadius,
ReadBookConfig.shadowDx,
ReadBookConfig.shadowDy,
ReadBookConfig.textShadowColor
)
}
//正文
val cPaint = TextPaint()
cPaint.color = ReadBookConfig.textColor
@@ -964,7 +979,17 @@ object ChapterProvider {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && AppConfig.optimizeRender) {
cPaint.isLinearText = true
}
if (ReadBookConfig.textItalic) {
cPaint.textSkewX = -0.25f
}
if (ReadBookConfig.textShadow) {
cPaint.setShadowLayer(
ReadBookConfig.shadowRadius,
ReadBookConfig.shadowDx,
ReadBookConfig.shadowDy,
ReadBookConfig.textShadowColor
)
}
return Pair(tPaint, cPaint)
}