fix: 高亮规则增加更多配置

This commit is contained in:
HapeLee
2026-06-10 01:45:32 +08:00
parent b27f068525
commit eef1f58f26
17 changed files with 5089 additions and 116 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -102,8 +102,6 @@ object PreferKey {
const val replaceEnableDefault = "replaceEnableDefault"
const val showBrightnessView = "showBrightnessView"
const val useUnderline = "useUnderline"
const val regexColorRules = "regexColorRules"
const val highlightRuleItems = "highlightRuleItems"
const val highlightRuleDialog = "highlightRuleDialog"
const val highlightRuleBookTitle = "highlightRuleBookTitle"
const val highlightRuleBracketNote = "highlightRuleBracketNote"
@@ -75,7 +75,7 @@ val appDb by lazy {
}
@Database(
version = 90,
version = 92,
exportSchema = true,
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
@@ -133,7 +133,9 @@ val appDb by lazy {
AutoMigration(from = 86, to = 87),
AutoMigration(from = 87, to = 88),
AutoMigration(from = 88, to = 89),
AutoMigration(from = 89, to = 90)
AutoMigration(from = 89, to = 90),
AutoMigration(from = 90, to = 91),
AutoMigration(from = 91, to = 92)
]
)
abstract class AppDatabase : RoomDatabase() {
@@ -30,6 +30,9 @@ interface HighlightRuleDao {
@Query("DELETE FROM highlightRules")
fun deleteAll()
@Query("DELETE FROM highlightRules WHERE configName IS NULL")
fun deleteGlobal()
@Query("SELECT COUNT(*) FROM highlightRules")
fun count(): Int
@@ -38,4 +41,10 @@ interface HighlightRuleDao {
deleteAll()
insertAll(rules)
}
@Transaction
fun replaceGlobal(rules: List<HighlightRule>) {
deleteGlobal()
insertAll(rules)
}
}
@@ -24,6 +24,8 @@ data class HighlightRule(
var bgImage: String? = null,
var bgImageFit: Int = 0,
var bgImageScale: Float = 1f,
var configName: String? = null,
var fontPath: String? = null,
) {
fun styleSummary(): String {
@@ -56,6 +58,9 @@ data class HighlightRule(
}
)
}
if (!fontPath.isNullOrBlank()) {
parts.add("自定义字体")
}
if (parts.isEmpty()) {
parts.add("无样式")
}
@@ -5,6 +5,7 @@ import io.legado.app.constant.AppLog
import io.legado.app.help.DefaultData
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ReadStyleResolver
import io.legado.app.ui.book.read.config.HighlightRuleStore
import io.legado.app.utils.FileUtils
import io.legado.app.utils.GSON
import io.legado.app.utils.compress.ZipUtils
@@ -118,7 +119,7 @@ class ReadStyleRepository {
val exportDir = appCtx.externalCache.getFile("readConfigExport")
exportDir.createFolderReplace()
val exportConfig = config.copy(
regexColorRules = ArrayList(config.regexColorRules.map { it.copy() })
highlightRules = ArrayList(config.highlightRules.map { it.copy() })
)
val exportFiles = arrayListOf<File>()
@@ -196,6 +197,10 @@ class ReadStyleRepository {
config.curTextColor()
config.curTextAccentColor()
config.curTextShadowColor()
if (config.highlightRules.isNotEmpty()) {
val targetConfigName = config.name.ifBlank { null }
HighlightRuleStore.saveForConfig(config.highlightRules, targetConfigName)
}
return config
}
@@ -13,7 +13,9 @@ import io.legado.app.constant.PreferKey
import io.legado.app.constant.ReadMenuBlurMode
import io.legado.app.constant.ReadMenuBlurStyle
import androidx.compose.runtime.State
import io.legado.app.data.entities.HighlightRule
import io.legado.app.data.repository.ReadStyleRepository
import io.legado.app.ui.book.read.config.HighlightRuleStore
import io.legado.app.help.DefaultData
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.ui.config.PrefDelegate
@@ -329,11 +331,6 @@ object ReadBookConfig {
val resolvedMenuBorderColor: Int
get() = if (ReadStyleResolver.isNightTheme()) readMenuBorderColorNight else readMenuBorderColor
val regexColorRules: ArrayList<RegexColorRule> get() = durConfig.regexColorRules
fun saveRegexColorRules() {
save()
}
val config get() = if (shareLayout) shareConfig else durConfig
@@ -776,7 +773,7 @@ object ReadBookConfig {
// endregion
fun getExportConfig(): Config {
val exportConfig = durConfig.copy(regexColorRules = ArrayList(durConfig.regexColorRules.map { it.copy() }))
val exportConfig = durConfig.copy(highlightRules = ArrayList(HighlightRuleStore.load()))
if (shareLayout) {
exportConfig.textFont = shareConfig.textFont
exportConfig.titleFont = shareConfig.titleFont
@@ -963,7 +960,7 @@ object ReadBookConfig {
var menuBottomHorizontalMargin: Int = 0,
@Transient
var menuBottomBottomMargin: Int = 0,
var regexColorRules: ArrayList<RegexColorRule> = arrayListOf()
var highlightRules: ArrayList<HighlightRule> = arrayListOf()
) {
@Transient
@@ -1082,7 +1079,7 @@ object ReadBookConfig {
"tipDividerColor" to tipDividerColor,
"headerMode" to headerMode,
"footerMode" to footerMode,
"regexColorRules" to regexColorRules.map { mapOf("name" to it.name, "pattern" to it.pattern, "color" to it.color, "fontPath" to it.fontPath) }
"highlightRules" to highlightRules.map { mapOf("id" to it.id, "name" to it.name, "pattern" to it.pattern, "sampleText" to it.sampleText, "targetScope" to it.targetScope, "enabled" to it.enabled, "position" to it.position, "textColor" to it.textColor, "bgColor" to it.bgColor, "underlineMode" to it.underlineMode, "underlineColor" to it.underlineColor, "underlineWidth" to it.underlineWidth, "underlineOffset" to it.underlineOffset, "underlineSvgPath" to it.underlineSvgPath, "bgImage" to it.bgImage, "bgImageFit" to it.bgImageFit, "bgImageScale" to it.bgImageScale, "configName" to it.configName, "fontPath" to it.fontPath) }
)
fun getBgPath(bgIndex: Int): String? {
@@ -1,8 +0,0 @@
package io.legado.app.help.config
data class RegexColorRule(
var name: String,
var pattern: String,
var color: Int,
var fontPath: String = ""
)
@@ -7,9 +7,7 @@ import io.legado.app.help.config.ReadBookConfig
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefString
import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.putPrefString
import splitties.init.appCtx
import java.io.File
@@ -27,21 +25,51 @@ object HighlightRuleStore {
private val dao get() = appDb.highlightRuleDao
fun load(): List<HighlightRule> {
migrateFromPrefsIfNeeded()
return dao.getAll()
val configName = ReadBookConfig.durConfig.name
return dao.getAll().filter { it.matchesConfig(configName) }
}
fun loadEnabled(): List<HighlightRule> {
migrateFromPrefsIfNeeded()
return dao.getEnabled()
val configName = ReadBookConfig.durConfig.name
return dao.getEnabled().filter { it.matchesConfig(configName) }
}
/**
* 保存当前排版的规则。
* 仅替换当前排版绑定的规则,不影响其他排版的规则。
*/
fun save(rules: List<HighlightRule>) {
val configName = ReadBookConfig.durConfig.name
saveForConfig(rules, configName.ifBlank { null })
}
fun saveForConfig(rules: List<HighlightRule>, configName: String?) {
val sanitized = rules.mapIndexed { index, rule ->
sanitizeRule(rule).copy(position = index)
}
dao.replaceAll(sanitized)
cleanupUnusedBgImages(sanitized)
if (configName.isNullOrBlank()) {
// 全局规则:只替换 configName 为 null 的规则
dao.replaceGlobal(sanitized)
} else {
// 按排版保存:只处理绑定到当前排版的规则,不动全局规则
val allRules = dao.getAll()
// 旧的绑定到当前排版的规则(不含全局规则)
val oldBound = allRules.filter {
!it.configName.isNullOrBlank() && it.matchesConfig(configName)
}
val newIds = sanitized.map { it.id }.toSet()
// 被移除的旧规则:从 configName 列表中去掉当前排版
// 未绑定任何排版时保留规则(configName="[]"),不删除
for (old in oldBound) {
if (old.id !in newIds) {
val remaining = old.configName.orEmpty().configNames().filter { it != configName }
dao.update(old.copy(configName = remaining.toJsonArray()))
}
}
// 插入所有规则(全局规则也一起,否则会被 replaceGlobal 删掉)
dao.insertAll(sanitized)
}
cleanupUnusedBgImages()
}
fun update(rule: HighlightRule) {
@@ -54,7 +82,12 @@ object HighlightRuleStore {
fun reset(): List<HighlightRule> {
val defaults = createDefaultRules()
dao.replaceAll(defaults)
val configName = ReadBookConfig.durConfig.name
if (configName.isBlank()) {
dao.replaceGlobal(defaults)
} else {
saveForConfig(defaults, configName)
}
return defaults
}
@@ -73,70 +106,14 @@ object HighlightRuleStore {
val restoredBgImage = restoreRuleBgImage(backupRootPath, safeRule.bgImage)
safeRule.copy(bgImage = restoredBgImage)
}
save(rules)
// 备份恢复是全量替换
dao.replaceAll(rules)
cleanupUnusedBgImages()
appCtx.putPrefBoolean(PreferKey.highlightRuleDialog, backupData.dialogEnabled)
appCtx.putPrefBoolean(PreferKey.highlightRuleBookTitle, backupData.bookTitleEnabled)
appCtx.putPrefBoolean(PreferKey.highlightRuleBracketNote, backupData.bracketNoteEnabled)
}
/**
* 从旧版 SharedPreferences 迁移数据(一次性)
*/
private fun migrateFromPrefsIfNeeded() {
if (dao.count() > 0) return
// 尝试从 SharedPreferences 读取旧数据
val stored = appCtx.getPrefString(PreferKey.highlightRuleItems)
if (!stored.isNullOrBlank()) {
val oldRules = GSON.fromJsonArray<LegacyHighlightRule>(stored).getOrNull()
if (!oldRules.isNullOrEmpty()) {
val migrated = oldRules.mapIndexed { index, old ->
sanitizeRule(
HighlightRule(
id = old.id,
name = old.name,
pattern = old.pattern,
sampleText = old.sampleText,
targetScope = old.targetScope,
enabled = old.enabled,
position = index,
textColor = old.textColor,
underlineMode = old.underlineMode,
underlineColor = old.underlineColor,
underlineWidth = old.underlineWidth,
underlineOffset = old.underlineOffset,
underlineSvgPath = old.underlineSvgPath,
bgImage = old.bgImage,
bgImageFit = old.bgImageFit,
bgImageScale = old.bgImageScale,
)
).copy(position = index)
}
dao.insertAll(migrated)
// 清除旧 SharedPreferences 数据
appCtx.putPrefString(PreferKey.highlightRuleItems, null)
return
}
}
// 尝试从旧版 RegexColorRule 迁移
migrateFromRegexColorRules()
}
private fun migrateFromRegexColorRules() {
val oldRules = ReadBookConfig.regexColorRules
if (oldRules.isEmpty()) return
val migrated = oldRules.mapIndexed { index, old ->
HighlightRule(
name = old.name,
pattern = old.pattern,
position = index,
textColor = old.color,
)
}
dao.insertAll(migrated)
oldRules.clear()
ReadBookConfig.save()
}
fun sanitizeRule(rule: HighlightRule): HighlightRule {
val name = runCatching { rule.name }.getOrNull().orEmpty()
val pattern = runCatching { rule.pattern }.getOrNull().orEmpty()
@@ -162,6 +139,8 @@ object HighlightRuleStore {
bgImage = runCatching { rule.bgImage }.getOrNull()?.takeIf { it.isNotBlank() },
bgImageFit = runCatching { rule.bgImageFit }.getOrDefault(0).coerceIn(0, 2),
bgImageScale = runCatching { rule.bgImageScale }.getOrDefault(1f).coerceIn(0.1f, 5f),
configName = runCatching { rule.configName }.getOrNull()?.takeIf { it.isNotBlank() },
fontPath = runCatching { rule.fontPath }.getOrNull()?.takeIf { it.isNotBlank() },
)
}
@@ -304,8 +283,9 @@ object HighlightRuleStore {
)
}
private fun cleanupUnusedBgImages(rules: List<HighlightRule>) {
val usedPaths = rules.mapNotNull { it.bgImage }
private fun cleanupUnusedBgImages() {
val allRules = dao.getAll()
val usedPaths = allRules.mapNotNull { it.bgImage }
.filter { it.isNotBlank() && !it.startsWith("assets://") }
.toSet()
val dir = File(appCtx.filesDir, "bg_images")
@@ -333,24 +313,34 @@ object HighlightRuleStore {
return targetFile.absolutePath
}
// region configName helpers
/**
* 旧版 SharedPreferences 数据结构(用于迁移)
* 判断规则是否适用于指定排版。
* configName 为 null 表示全局规则(适用于所有排版)。
* configName 为 JSON 数组字符串,如 '["日间","夜间"]'。
*/
private data class LegacyHighlightRule(
val id: String = "",
val name: String = "",
val pattern: String = "",
val sampleText: String = "",
val targetScope: Int = 0,
val enabled: Boolean = true,
val textColor: Int? = null,
val underlineMode: Int = 0,
val underlineColor: Int? = null,
val underlineWidth: Float = 1f,
val underlineOffset: Float = 2f,
val underlineSvgPath: String? = null,
val bgImage: String? = null,
val bgImageFit: Int = 0,
val bgImageScale: Float = 1f,
)
private fun HighlightRule.matchesConfig(configName: String): Boolean {
val cn = this.configName
if (cn.isNullOrBlank()) return true // 全局规则
return cn.configNames().contains(configName)
}
/**
* 解析 configName JSON 数组为列表。
*/
fun String.configNames(): List<String> {
return runCatching {
GSON.fromJsonArray<String>(this).getOrNull() ?: emptyList()
}.getOrElse { emptyList() }
}
/**
* 将排版名列表转为 JSON 数组字符串。
*/
fun List<String>.toJsonArray(): String {
return GSON.toJson(this)
}
// endregion
}
@@ -17,6 +17,7 @@ interface TextBaseColumn : BaseColumn {
val bgImage: String
val bgImageFit: Int
val bgImageScale: Float
val fontPath: String
var selected: Boolean
var isSearchResult: Boolean
}
@@ -4,11 +4,15 @@ import android.graphics.Canvas
import android.graphics.Typeface
import android.os.Build
import androidx.annotation.Keep
import androidx.core.net.toUri
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.entities.TextLine
import io.legado.app.ui.book.read.page.entities.TextLine.Companion.emptyTextLine
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.isContentScheme
import splitties.init.appCtx
import java.io.File
@Keep
data class TextColumn(
@@ -25,6 +29,7 @@ data class TextColumn(
override val bgImage: String = "",
override val bgImageFit: Int = 0,
override val bgImageScale: Float = 1f,
override val fontPath: String = "",
) : TextBaseColumn {
override var textLine: TextLine = emptyTextLine
@@ -105,7 +110,29 @@ data class TextColumn(
}
private fun getCustomTypeface(): Typeface? {
// TODO: HighlightRule 不再存储 fontPath,需要重新设计自定义字体方案
return null
if (fontPath.isEmpty()) return null
return typefaceCache.getOrPut(fontPath) {
loadTypeface(fontPath)
}
}
companion object {
private val typefaceCache = HashMap<String, Typeface?>()
private fun loadTypeface(fontPath: String): Typeface? {
return runCatching {
when {
fontPath.isContentScheme() -> {
appCtx.contentResolver
.openFileDescriptor(fontPath.toUri(), "r")!!
.use { Typeface.Builder(it.fileDescriptor).build() }
}
fontPath.isNotEmpty() -> {
Typeface.Builder(File(fontPath)).build()
}
else -> null
}
}.getOrNull()
}
}
}
@@ -30,6 +30,7 @@ data class TextHtmlColumn(
override val bgImage: String = "",
override val bgImageFit: Int = 0,
override val bgImageScale: Float = 1f,
override val fontPath: String = "",
) : TextBaseColumn {
override val textColor: Int? get() = mTextColor
@@ -14,7 +14,8 @@ data class CharStyle(
val bgImage: String = "",
val bgImageFit: Int = 0,
val bgImageScale: Float = 1f,
val fontPath: String = "",
) {
val hasStyle: Boolean
get() = textColor != null || bgColor != null || underlineMode != 0 || bgImage.isNotEmpty()
get() = textColor != null || bgColor != null || underlineMode != 0 || bgImage.isNotEmpty() || fontPath.isNotEmpty()
}
@@ -1240,7 +1240,8 @@ class TextChapterLayout(
underlineSvgPath = style?.underlineSvgPath ?: "",
bgImage = style?.bgImage ?: "",
bgImageFit = style?.bgImageFit ?: 0,
bgImageScale = style?.bgImageScale ?: 1f
bgImageScale = style?.bgImageScale ?: 1f,
fontPath = style?.fontPath ?: ""
)
}
}
@@ -1374,7 +1375,8 @@ class TextChapterLayout(
underlineSvgPath = rule.underlineSvgPath.orEmpty(),
bgImage = rule.bgImage.orEmpty(),
bgImageFit = rule.bgImageFit,
bgImageScale = rule.bgImageScale
bgImageScale = rule.bgImageScale,
fontPath = rule.fontPath.orEmpty()
)
compiled.regex.findAll(text).forEach { match ->
for (i in match.range) {
@@ -32,6 +32,7 @@ import io.legado.app.R
import io.legado.app.data.entities.HighlightRule
import io.legado.app.ui.book.read.ReadBookIntent
import io.legado.app.ui.book.read.config.HighlightRuleStore
import io.legado.app.ui.book.read.config.HighlightRuleStore.configNames
import io.legado.app.ui.widget.components.TinySwitch
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
@@ -166,9 +167,10 @@ private fun HighlightRuleItem(
onEditClick: () -> Unit,
onDeleteClick: () -> Unit,
) {
val configLabel = rule.configName?.configNames()?.joinToString("") ?: "全局"
TinySettingItem(
title = rule.name.ifBlank { rule.displayPattern() },
description = rule.styleSummary(),
description = "${rule.styleSummary()} · $configLabel",
onClick = onEditClick,
trailingContent = {
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -1,9 +1,11 @@
package io.legado.app.ui.book.read.sheet
import android.content.Intent
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -11,6 +13,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.FilterChip
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.MaterialTheme
@@ -20,6 +23,7 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -35,7 +39,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.legado.app.R
import io.legado.app.data.entities.HighlightRule
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.ui.book.read.config.HighlightRuleStore
import io.legado.app.ui.book.read.config.HighlightRuleStore.configNames
import io.legado.app.ui.book.read.config.HighlightRuleStore.toJsonArray
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.AppTextField
import io.legado.app.ui.widget.components.SectionTitle
@@ -47,6 +54,7 @@ import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
import io.legado.app.ui.widget.components.settingItem.TinySliderSettingItem
import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
import io.legado.app.ui.widget.components.text.AppText
import kotlinx.coroutines.launch
import splitties.init.appCtx
import java.io.File
@@ -87,10 +95,21 @@ fun HighlightRuleEditSheet(
var bgImageScale by remember { mutableFloatStateOf(initial.bgImageScale) }
var hasBgImage by remember { mutableStateOf(initial.bgImage?.isNotBlank() == true) }
// Config binding state — empty set = global (applies to all configs)
val allConfigNames = remember { ReadBookConfig.configList.map { it.name }.filter { it.isNotBlank() } }
var configNames by remember {
mutableStateOf(initial.configName.orEmpty().configNames().toSet())
}
// Font state
var hasFont by remember { mutableStateOf(initial.fontPath?.isNotBlank() == true) }
var fontPath by remember { mutableStateOf(initial.fontPath.orEmpty()) }
// Color picker state
var showTextColorPicker by remember { mutableStateOf(false) }
var showBgColorPicker by remember { mutableStateOf(false) }
var showUnderlineColorPicker by remember { mutableStateOf(false) }
var showFontSelect by remember { mutableStateOf(false) }
// Validation
var patternError by remember { mutableStateOf<String?>(null) }
@@ -147,6 +166,8 @@ fun HighlightRuleEditSheet(
bgImage = if (hasBgImage) bgImage.ifBlank { null } else null,
bgImageFit = bgImageFit,
bgImageScale = bgImageScale,
configName = if (configNames.isEmpty()) null else configNames.toList().toJsonArray(),
fontPath = if (hasFont) fontPath.ifBlank { null } else null,
)
)
onSave(sanitized)
@@ -348,7 +369,56 @@ fun HighlightRuleEditSheet(
)
}
// === Section 3: Preview ===
// === Section 3: Config Binding ===
if (allConfigNames.isNotEmpty()) {
SectionTitle("应用排版")
AppText(
text = if (configNames.isEmpty()) "全局(所有排版生效)"
else "已选: ${configNames.joinToString("、")}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
)
androidx.compose.foundation.layout.FlowRow(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
// Global toggle
FilterChip(
selected = configNames.isEmpty(),
onClick = { configNames = emptySet() },
label = { AppText("全局") },
)
allConfigNames.forEach { cn ->
FilterChip(
selected = cn in configNames,
onClick = {
configNames = if (cn in configNames) configNames - cn
else configNames + cn
},
label = { AppText(cn) },
)
}
}
}
// === Section 4: Font ===
SectionTitle("字体替换")
TinySwitchSettingItem(
title = "自定义字体",
checked = hasFont,
onCheckedChange = { hasFont = it },
)
if (hasFont) {
TinyClickableSettingItem(
title = stringResource(R.string.select_font),
description = fontPath.ifBlank { null }?.let { File(it).name },
onClick = { showFontSelect = true },
)
}
// === Section 5: Preview ===
SectionTitle(stringResource(R.string.preview_effect))
AppTextField(
@@ -408,6 +478,32 @@ fun HighlightRuleEditSheet(
},
)
}
// Font selector
if (showFontSelect) {
val readSettingsRepository: io.legado.app.data.repository.ReadSettingsRepository =
org.koin.compose.koinInject()
val scope = rememberCoroutineScope()
val fontFolderLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.OpenDocumentTree()
) { uri ->
uri?.let {
context.contentResolver.takePersistableUriPermission(
it, Intent.FLAG_GRANT_READ_URI_PERMISSION
)
scope.launch {
readSettingsRepository.setFontFolder(it.toString())
}
}
}
FontSelectSheet(
show = true,
onDismissRequest = { showFontSelect = false },
onSelectFont = { fontPath = it; showFontSelect = false },
onSelectSystemTypeface = { fontPath = ""; showFontSelect = false },
onOpenFolderPicker = { fontFolderLauncher.launch(null) },
)
}
}
@Composable