fix: 高亮逻辑问题

This commit is contained in:
HapeLee
2026-06-12 12:55:47 +08:00
parent 35875f7dd3
commit d25568d99a
2 changed files with 54 additions and 38 deletions
@@ -32,13 +32,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.res.stringArrayResource
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.legado.app.R import io.legado.app.R
@@ -73,53 +73,67 @@ fun HighlightRuleEditSheet(
onSave: (HighlightRule) -> Unit, onSave: (HighlightRule) -> Unit,
) { ) {
val isNew = rule == null val isNew = rule == null
val initial = rule ?: HighlightRule() val initial = remember(show, rule) { rule ?: HighlightRule() }
val context = LocalContext.current val context = LocalContext.current
// Rule info state // Rule info state
var pattern by remember { mutableStateOf(initial.pattern) } var pattern by remember(show, rule) { mutableStateOf(initial.pattern) }
var name by remember { mutableStateOf(initial.name) } var name by remember(show, rule) { mutableStateOf(initial.name) }
var targetScope by remember { mutableIntStateOf(initial.targetScope) } var targetScope by remember(show, rule) { mutableIntStateOf(initial.targetScope) }
var enabled by remember { mutableStateOf(initial.enabled) } var enabled by remember(show, rule) { mutableStateOf(initial.enabled) }
var sampleText by remember { var sampleText by remember(show, rule) {
mutableStateOf(initial.sampleText.ifBlank { "她轻声说:今晚就出发。" }) mutableStateOf(initial.sampleText.ifBlank { "她轻声说:今晚就出发。" })
} }
// Style state // Style state
var textColor by remember { mutableIntStateOf(initial.textColor ?: 0xFF63C37D.toInt()) } var textColor by remember(show, rule) {
var hasTextColor by remember { mutableStateOf(initial.textColor != null) } mutableIntStateOf(
var bgColor by remember { mutableIntStateOf(initial.bgColor ?: 0x20FFEB3B) } initial.textColor ?: 0xFF63C37D.toInt()
var hasBgColor by remember { mutableStateOf(initial.bgColor != null) } )
var hasUnderline by remember { mutableStateOf(initial.underlineMode > 0) } }
var underlineMode by remember { mutableIntStateOf(if (initial.underlineMode > 0) initial.underlineMode else 1) } var hasTextColor by remember(show, rule) { mutableStateOf(initial.textColor != null) }
var underlineColor by remember { mutableIntStateOf(initial.underlineColor ?: 0xFF63C37D.toInt()) } var bgColor by remember(show, rule) { mutableIntStateOf(initial.bgColor ?: 0x20FFEB3B) }
var hasUnderlineColor by remember { mutableStateOf(initial.underlineColor != null) } var hasBgColor by remember(show, rule) { mutableStateOf(initial.bgColor != null) }
var underlineWidth by remember { mutableFloatStateOf(initial.underlineWidth) } var hasUnderline by remember(show, rule) { mutableStateOf(initial.underlineMode > 0) }
var underlineOffset by remember { mutableFloatStateOf(initial.underlineOffset) } var underlineMode by remember(
var underlineSvgPath by remember { mutableStateOf(initial.underlineSvgPath.orEmpty()) } show,
var bgImage by remember { mutableStateOf(initial.bgImage.orEmpty()) } rule
var bgImageFit by remember { mutableIntStateOf(initial.bgImageFit) } ) { mutableIntStateOf(if (initial.underlineMode > 0) initial.underlineMode else 1) }
var bgImageScale by remember { mutableFloatStateOf(initial.bgImageScale) } var underlineColor by remember(show, rule) {
var hasBgImage by remember { mutableStateOf(initial.bgImage?.isNotBlank() == true) } mutableIntStateOf(
initial.underlineColor ?: 0xFF63C37D.toInt()
)
}
var hasUnderlineColor by remember(show, rule) { mutableStateOf(initial.underlineColor != null) }
var underlineWidth by remember(show, rule) { mutableFloatStateOf(initial.underlineWidth) }
var underlineOffset by remember(show, rule) { mutableFloatStateOf(initial.underlineOffset) }
var underlineSvgPath by remember(
show,
rule
) { mutableStateOf(initial.underlineSvgPath.orEmpty()) }
var bgImage by remember(show, rule) { mutableStateOf(initial.bgImage.orEmpty()) }
var bgImageFit by remember(show, rule) { mutableIntStateOf(initial.bgImageFit) }
var bgImageScale by remember(show, rule) { mutableFloatStateOf(initial.bgImageScale) }
var hasBgImage by remember(show, rule) { mutableStateOf(initial.bgImage?.isNotBlank() == true) }
// Config binding state — empty set = global (applies to all configs) // Config binding state — empty set = global (applies to all configs)
val allConfigNames = remember { ReadBookConfig.configList.map { it.name }.filter { it.isNotBlank() } } val allConfigNames = remember { ReadBookConfig.configList.map { it.name }.filter { it.isNotBlank() } }
var configNames by remember { var configNames by remember(show, rule) {
mutableStateOf(initial.configName.orEmpty().configNames().toSet()) mutableStateOf(initial.configName.orEmpty().configNames().toSet())
} }
// Font state // Font state
var hasFont by remember { mutableStateOf(initial.fontPath?.isNotBlank() == true) } var hasFont by remember(show, rule) { mutableStateOf(initial.fontPath?.isNotBlank() == true) }
var fontPath by remember { mutableStateOf(initial.fontPath.orEmpty()) } var fontPath by remember(show, rule) { mutableStateOf(initial.fontPath.orEmpty()) }
// Color picker state // Color picker state
var showTextColorPicker by remember { mutableStateOf(false) } var showTextColorPicker by remember(show, rule) { mutableStateOf(false) }
var showBgColorPicker by remember { mutableStateOf(false) } var showBgColorPicker by remember(show, rule) { mutableStateOf(false) }
var showUnderlineColorPicker by remember { mutableStateOf(false) } var showUnderlineColorPicker by remember(show, rule) { mutableStateOf(false) }
var showFontSelect by remember { mutableStateOf(false) } var showFontSelect by remember(show, rule) { mutableStateOf(false) }
// Validation // Validation
var patternError by remember { mutableStateOf<String?>(null) } var patternError by remember(show, rule) { mutableStateOf<String?>(null) }
// SAF image picker // SAF image picker
val imagePicker = rememberLauncherForActivityResult( val imagePicker = rememberLauncherForActivityResult(
@@ -154,22 +154,22 @@ fun SpeakEngineConfigSheet(
) { ) {
val items = state.ttsEngineItems val items = state.ttsEngineItems
val selectedValue = state.selectedTtsEngine val selectedValue = state.selectedTtsEngine
var pendingEngineValue by remember { mutableStateOf<String?>(null) } var pendingEngineSelection by remember { mutableStateOf<PendingSpeakEngineSelection?>(null) }
AppAlertDialog( AppAlertDialog(
show = pendingEngineValue != null, show = pendingEngineSelection != null,
onDismissRequest = { pendingEngineValue = null }, onDismissRequest = { pendingEngineSelection = null },
title = stringResource(R.string.speak_engine), title = stringResource(R.string.speak_engine),
text = stringResource(R.string.speak_engine_apply_scope), text = stringResource(R.string.speak_engine_apply_scope),
confirmText = stringResource(R.string.general), confirmText = stringResource(R.string.general),
onConfirm = { onConfirm = {
onIntent(ReadBookIntent.ApplySpeakEngine(pendingEngineValue)) onIntent(ReadBookIntent.ApplySpeakEngine(pendingEngineSelection?.value))
pendingEngineValue = null pendingEngineSelection = null
}, },
dismissText = stringResource(R.string.book), dismissText = stringResource(R.string.book),
onDismiss = { onDismiss = {
onIntent(ReadBookIntent.ApplySpeakEnginePerBook(pendingEngineValue)) onIntent(ReadBookIntent.ApplySpeakEnginePerBook(pendingEngineSelection?.value))
pendingEngineValue = null pendingEngineSelection = null
}, },
) )
@@ -232,7 +232,7 @@ fun SpeakEngineConfigSheet(
} else { } else {
null null
}, },
onClick = { pendingEngineValue = item.value }, onClick = { pendingEngineSelection = PendingSpeakEngineSelection(item.value) },
onLongClick = if (httpTtsId != null && !item.loginUrl.isNullOrBlank()) { onLongClick = if (httpTtsId != null && !item.loginUrl.isNullOrBlank()) {
{ onIntent(ReadBookIntent.OpenHttpTtsLogin(httpTtsId)) } { onIntent(ReadBookIntent.OpenHttpTtsLogin(httpTtsId)) }
} else null, } else null,
@@ -256,6 +256,8 @@ fun SpeakEngineConfigSheet(
} }
} }
private data class PendingSpeakEngineSelection(val value: String?)
@Composable @Composable
fun HttpTtsEditSheet( fun HttpTtsEditSheet(
show: Boolean, show: Boolean,