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
@@ -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