fix: 阅读菜单模糊着色、调色板样式、标题胶囊布局及主题覆盖修复
This commit is contained in:
@@ -308,6 +308,8 @@ object PreferKey {
|
||||
const val readMenuBottomBarBlurStyle = "readMenuBottomBarBlurStyle"
|
||||
const val readMenuBlurRadius = "readMenuBlurRadius"
|
||||
const val readMenuBlurAlpha = "readMenuBlurAlpha"
|
||||
const val readMenuBlurColor = "readMenuBlurColor"
|
||||
const val readMenuPaletteStyle = "readMenuPaletteStyle"
|
||||
const val readMenuLensRadius = "readMenuLensRadius"
|
||||
const val readMenuBorderWidth = "readMenuBorderWidth"
|
||||
const val readMenuBorderColor = "readMenuBorderColor"
|
||||
|
||||
@@ -94,6 +94,8 @@ data class ReadPreferences(
|
||||
val readMenuBottomBarBlurStyle: Int = ReadMenuBlurStyle.Solid,
|
||||
val readMenuBlurRadius: Int = 24,
|
||||
val readMenuBlurAlpha: Int = 60,
|
||||
val readMenuBlurColor: Int = 0,
|
||||
val readMenuPaletteStyle: String = "",
|
||||
val readMenuLensRadius: Float = 24f,
|
||||
val readMenuBorderWidth: Int = 0,
|
||||
val readMenuBorderColor: Int = 0,
|
||||
@@ -335,6 +337,12 @@ class ReadSettingsRepository(
|
||||
suspend fun setReadMenuBlurAlpha(value: Int) =
|
||||
settingsRepository.putInt(PreferKey.readMenuBlurAlpha, value.coerceIn(0, 100))
|
||||
|
||||
suspend fun setReadMenuBlurColor(value: Int) =
|
||||
settingsRepository.putInt(PreferKey.readMenuBlurColor, value)
|
||||
|
||||
suspend fun setReadMenuPaletteStyle(value: String) =
|
||||
settingsRepository.putString(PreferKey.readMenuPaletteStyle, value)
|
||||
|
||||
suspend fun setReadMenuLensRadius(value: Float) =
|
||||
settingsRepository.putFloat(PreferKey.readMenuLensRadius, value.coerceIn(0f, 48f))
|
||||
|
||||
@@ -446,6 +454,7 @@ class ReadSettingsRepository(
|
||||
readMenuBottomBarBlurMode = this[Keys.ReadMenuBottomBarBlurMode]
|
||||
?: ReadMenuBlurMode.None,
|
||||
readMenuTopBarLiquidGlassButtons = this[Keys.ReadMenuTopBarLiquidGlassButtons] ?: false,
|
||||
readMenuTopBarTitleCapsule = this[Keys.ReadMenuTopBarTitleCapsule] ?: false,
|
||||
readMenuBottomBarLiquidGlassButtons = this[Keys.ReadMenuBottomBarLiquidGlassButtons]
|
||||
?: false,
|
||||
readMenuTopBarBlurStyle = this[Keys.ReadMenuTopBarBlurStyle]
|
||||
@@ -454,6 +463,8 @@ class ReadSettingsRepository(
|
||||
?: ReadMenuBlurStyle.Solid,
|
||||
readMenuBlurRadius = this[Keys.ReadMenuBlurRadius] ?: 24,
|
||||
readMenuBlurAlpha = this[Keys.ReadMenuBlurAlpha] ?: 60,
|
||||
readMenuBlurColor = this[Keys.ReadMenuBlurColor] ?: 0,
|
||||
readMenuPaletteStyle = this[Keys.ReadMenuPaletteStyle] ?: "",
|
||||
readMenuLensRadius = this[Keys.ReadMenuLensRadius] ?: 24f,
|
||||
readMenuBorderWidth = this[Keys.ReadMenuBorderWidth] ?: 0,
|
||||
readMenuBorderColor = this[Keys.ReadMenuBorderColor] ?: 0,
|
||||
@@ -540,12 +551,16 @@ class ReadSettingsRepository(
|
||||
val ReadMenuBottomBarBlurMode = intPreferencesKey(PreferKey.readMenuBottomBarBlurMode)
|
||||
val ReadMenuTopBarLiquidGlassButtons =
|
||||
booleanPreferencesKey(PreferKey.readMenuTopBarLiquidGlassButtons)
|
||||
val ReadMenuTopBarTitleCapsule =
|
||||
booleanPreferencesKey(PreferKey.readMenuTopBarTitleCapsule)
|
||||
val ReadMenuBottomBarLiquidGlassButtons =
|
||||
booleanPreferencesKey(PreferKey.readMenuBottomBarLiquidGlassButtons)
|
||||
val ReadMenuTopBarBlurStyle = intPreferencesKey(PreferKey.readMenuTopBarBlurStyle)
|
||||
val ReadMenuBottomBarBlurStyle = intPreferencesKey(PreferKey.readMenuBottomBarBlurStyle)
|
||||
val ReadMenuBlurRadius = intPreferencesKey(PreferKey.readMenuBlurRadius)
|
||||
val ReadMenuBlurAlpha = intPreferencesKey(PreferKey.readMenuBlurAlpha)
|
||||
val ReadMenuBlurColor = intPreferencesKey(PreferKey.readMenuBlurColor)
|
||||
val ReadMenuPaletteStyle = stringPreferencesKey(PreferKey.readMenuPaletteStyle)
|
||||
val ReadMenuLensRadius = floatPreferencesKey(PreferKey.readMenuLensRadius)
|
||||
val ReadMenuBorderWidth = intPreferencesKey(PreferKey.readMenuBorderWidth)
|
||||
val ReadMenuBorderColor = intPreferencesKey(PreferKey.readMenuBorderColor)
|
||||
|
||||
@@ -25,12 +25,13 @@ import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
sealed interface ChangeSourceSearchEvent {
|
||||
data object Started : ChangeSourceSearchEvent
|
||||
data class Started(val totalSources: Int) : ChangeSourceSearchEvent
|
||||
data class Progress(
|
||||
val processedSources: Int,
|
||||
val totalSources: Int,
|
||||
@@ -69,23 +70,20 @@ class ChangeSourceSearchUseCase(
|
||||
if (bookSourceParts.isEmpty()) {
|
||||
throw io.legado.app.exception.NoStackTraceException("启用书源为空")
|
||||
}
|
||||
val sources = bookSourceParts.mapNotNull { it.getBookSource() }
|
||||
if (sources.isEmpty()) {
|
||||
throw io.legado.app.exception.NoStackTraceException("启用书源为空")
|
||||
}
|
||||
|
||||
tocMap.clear()
|
||||
bookMap.clear()
|
||||
tocMapChapterCount.set(0)
|
||||
|
||||
emit(ChangeSourceSearchEvent.Started)
|
||||
val totalSources = bookSourceParts.size
|
||||
emit(ChangeSourceSearchEvent.Started(totalSources))
|
||||
|
||||
var processedSources = 0
|
||||
var resultCount = 0
|
||||
val totalSources = sources.size
|
||||
val concurrency = threadCount.coerceAtLeast(1)
|
||||
|
||||
sources.asFlow()
|
||||
bookSourceParts.asFlow()
|
||||
.mapNotNull { it.getBookSource() }
|
||||
.flatMapMerge(concurrency) { source ->
|
||||
flow {
|
||||
val books = try {
|
||||
@@ -231,7 +229,7 @@ class ChangeSourceSearchUseCase(
|
||||
contentProcessor
|
||||
)
|
||||
}
|
||||
return null
|
||||
return book.toSearchBook()
|
||||
}
|
||||
|
||||
private suspend fun loadBookWordCount(
|
||||
|
||||
@@ -236,6 +236,8 @@ object ReadBookConfig {
|
||||
var readMenuBottomBarBlurStyle by clampedPrefDelegate(PreferKey.readMenuBottomBarBlurStyle, ReadMenuBlurStyle.Solid, 0..1)
|
||||
var readMenuBlurRadius by clampedPrefDelegate(PreferKey.readMenuBlurRadius, 24, 0..32)
|
||||
var readMenuBlurAlpha by clampedPrefDelegate(PreferKey.readMenuBlurAlpha, 60, 0..100)
|
||||
var readMenuBlurColor by prefDelegate(PreferKey.readMenuBlurColor, 0)
|
||||
var readMenuPaletteStyle: String = ""
|
||||
var readMenuLensRadius by clampedPrefDelegate(PreferKey.readMenuLensRadius, 24f, 0f..48f)
|
||||
var readMenuBorderWidth by clampedPrefDelegate(PreferKey.readMenuBorderWidth, 0, 0..4)
|
||||
var titleBarIconPosition by clampedPrefDelegate(PreferKey.titleBarIconPosition, 0, 0..3)
|
||||
|
||||
+74
-5
@@ -70,8 +70,11 @@ class ChangeBookSourceComposeViewModel(
|
||||
val loadWordCount: Boolean get() = ChangeSourceConfig.loadWordCount
|
||||
|
||||
// Internal state
|
||||
private val chapterNumRegex = "^\\[(\\d+)]".toRegex()
|
||||
private var searchJob: Job? = null
|
||||
private var oldBook: Book? = null
|
||||
private var bookName: String = ""
|
||||
private var bookAuthor: String = ""
|
||||
private var fromReadBookActivity: Boolean = false
|
||||
private var screenKey: String = ""
|
||||
private val searchResults = mutableListOf<SearchBook>()
|
||||
@@ -86,15 +89,42 @@ class ChangeBookSourceComposeViewModel(
|
||||
|
||||
fun initData(name: String, author: String, book: Book, fromReadBookActivity: Boolean) {
|
||||
this.oldBook = book
|
||||
this.bookName = name
|
||||
this.bookAuthor = author
|
||||
this.fromReadBookActivity = fromReadBookActivity
|
||||
if (searchJob?.isActive != true) {
|
||||
startSearch()
|
||||
viewModelScope.launch {
|
||||
val dbBooks = getDbSearchBooks()
|
||||
if (dbBooks.isNotEmpty()) {
|
||||
searchResults.clear()
|
||||
searchResults.addAll(dbBooks)
|
||||
searchResults.forEach { bookMap[it.primaryStr()] = it }
|
||||
filterResults()
|
||||
} else {
|
||||
startSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDbSearchBooks(): List<SearchBook> {
|
||||
val scopeRaw = ChangeSourceConfig.searchScope
|
||||
return if (screenKey.isEmpty()) {
|
||||
io.legado.app.data.appDb.searchBookDao.changeSourceByGroup(
|
||||
bookName, bookAuthor, scopeRaw
|
||||
)
|
||||
} else {
|
||||
io.legado.app.data.appDb.searchBookDao.changeSourceSearch(
|
||||
bookName, bookAuthor, screenKey, scopeRaw
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun startSearch() {
|
||||
val book = oldBook ?: return
|
||||
stopSearch()
|
||||
// 清除 DB 中的旧记录
|
||||
io.legado.app.data.appDb.searchBookDao.clear(bookName, bookAuthor)
|
||||
searchResults.clear()
|
||||
bookMap.clear()
|
||||
tocMap.clear()
|
||||
@@ -113,6 +143,7 @@ class ChangeBookSourceComposeViewModel(
|
||||
when (event) {
|
||||
is ChangeSourceSearchEvent.Started -> {
|
||||
_isSearching.value = true
|
||||
totalSourceCount = event.totalSources
|
||||
}
|
||||
|
||||
is ChangeSourceSearchEvent.Progress -> {
|
||||
@@ -123,6 +154,8 @@ class ChangeBookSourceComposeViewModel(
|
||||
is ChangeSourceSearchEvent.Result -> {
|
||||
searchResults.add(event.searchBook)
|
||||
bookMap[event.searchBook.primaryStr()] = event.searchBook
|
||||
// 持久化到 DB
|
||||
io.legado.app.data.appDb.searchBookDao.insert(event.searchBook)
|
||||
filterResults()
|
||||
}
|
||||
|
||||
@@ -179,12 +212,24 @@ class ChangeBookSourceComposeViewModel(
|
||||
it.name.contains(screenKey) || it.originName.contains(screenKey)
|
||||
}
|
||||
}
|
||||
val sorted = filtered.sortedWith(
|
||||
val comparator = if (ChangeSourceConfig.loadWordCount) {
|
||||
compareByDescending<SearchBook> { ObservableSourceConfig.getBookScore(it) }
|
||||
.thenByDescending { io.legado.app.help.config.SourceConfig.getSourceScore(it.origin) }
|
||||
.thenByDescending { it.chapterWordCount > 1000 }
|
||||
.thenByDescending { getChapterNum(it.chapterWordCountText) }
|
||||
.thenByDescending { it.chapterWordCount }
|
||||
.thenBy { it.originOrder }
|
||||
} else {
|
||||
compareByDescending<SearchBook> { ObservableSourceConfig.getBookScore(it) }
|
||||
.thenByDescending { io.legado.app.help.config.SourceConfig.getSourceScore(it.origin) }
|
||||
.thenBy { it.originOrder }
|
||||
)
|
||||
_searchDataFlow.value = sorted
|
||||
}
|
||||
_searchDataFlow.value = filtered.sortedWith(comparator)
|
||||
}
|
||||
|
||||
private fun getChapterNum(text: String?): Int {
|
||||
if (text.isNullOrBlank()) return 0
|
||||
return chapterNumRegex.find(text)?.groupValues?.get(1)?.toIntOrNull() ?: 0
|
||||
}
|
||||
|
||||
fun getToc(
|
||||
@@ -247,7 +292,14 @@ class ChangeBookSourceComposeViewModel(
|
||||
fun refresh() {
|
||||
searchResults.clear()
|
||||
bookMap.clear()
|
||||
startSearch()
|
||||
val dbBooks = getDbSearchBooks()
|
||||
if (dbBooks.isNotEmpty()) {
|
||||
searchResults.addAll(dbBooks)
|
||||
searchResults.forEach { bookMap[it.primaryStr()] = it }
|
||||
filterResults()
|
||||
} else {
|
||||
startSearch()
|
||||
}
|
||||
}
|
||||
|
||||
// Source actions
|
||||
@@ -340,6 +392,23 @@ class ChangeBookSourceComposeViewModel(
|
||||
saveScope()
|
||||
}
|
||||
|
||||
fun applyScopeSelection(selection: io.legado.app.ui.book.search.ScopeSelection) {
|
||||
if (selection.isSourceScope) {
|
||||
if (selection.sources.isEmpty()) {
|
||||
searchScope.update("")
|
||||
} else {
|
||||
searchScope.updateSources(selection.sources)
|
||||
}
|
||||
} else {
|
||||
if (selection.groupNames.isEmpty()) {
|
||||
searchScope.update("")
|
||||
} else {
|
||||
searchScope.update(selection.groupNames)
|
||||
}
|
||||
}
|
||||
saveScope()
|
||||
}
|
||||
|
||||
private fun saveScope() {
|
||||
ChangeSourceConfig.searchScope = searchScope.toString()
|
||||
_scopeUiState.value = ScopeUiState(
|
||||
|
||||
@@ -46,12 +46,14 @@ private fun rememberReadBookColorTheme(
|
||||
preferences: ReadPreferences,
|
||||
): ThemeOverrideState? {
|
||||
val isAppDark = LegadoTheme.isDark
|
||||
val paletteStyle = preferences.readMenuPaletteStyle
|
||||
return when (preferences.readBarStyle) {
|
||||
1 -> rememberReadBackgroundTheme(styleConfig, isAppDark)
|
||||
1 -> rememberReadBackgroundTheme(styleConfig, isAppDark, paletteStyle)
|
||||
2 -> rememberCustomReadMenuTheme(
|
||||
styleConfig = styleConfig,
|
||||
preferences = preferences,
|
||||
isAppDark = isAppDark,
|
||||
paletteStyle = paletteStyle,
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
@@ -61,6 +63,7 @@ private fun rememberReadBookColorTheme(
|
||||
private fun rememberReadBackgroundTheme(
|
||||
styleConfig: ReadBookStyleConfig,
|
||||
isAppDark: Boolean,
|
||||
paletteStyle: String,
|
||||
): ThemeOverrideState? {
|
||||
val background = remember(styleConfig, isAppDark) {
|
||||
runCatching { ReadStyleResolver.currentBackground(ReadBookConfig.durConfig) }.getOrNull()
|
||||
@@ -85,6 +88,7 @@ private fun rememberReadBackgroundTheme(
|
||||
backgroundColor = surfaceColor,
|
||||
containerColor = null,
|
||||
fallbackDark = isAppDark,
|
||||
paletteStyle = paletteStyle,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -93,6 +97,7 @@ private fun rememberCustomReadMenuTheme(
|
||||
styleConfig: ReadBookStyleConfig,
|
||||
preferences: ReadPreferences,
|
||||
isAppDark: Boolean,
|
||||
paletteStyle: String,
|
||||
): ThemeOverrideState {
|
||||
val menuBackgroundColor = remember(
|
||||
styleConfig,
|
||||
@@ -127,6 +132,7 @@ private fun rememberCustomReadMenuTheme(
|
||||
menuBackgroundColor = menuBackgroundColor,
|
||||
menuContainerColor = menuContainerColor,
|
||||
isDark = isAppDark,
|
||||
paletteStyle = paletteStyle,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,11 +142,13 @@ private fun rememberCustomReadMenuTheme(
|
||||
containerColor = null,
|
||||
fallbackDark = isAppDark,
|
||||
deriveDarkFromColor = false,
|
||||
paletteStyle = paletteStyle,
|
||||
) ?: buildReadThemeOverride(
|
||||
seedColor = accentColor,
|
||||
backgroundColor = null,
|
||||
containerColor = null,
|
||||
isDark = isAppDark,
|
||||
paletteStyle = paletteStyle,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -150,13 +158,15 @@ private fun rememberCustomReadMenuThemeOverride(
|
||||
menuBackgroundColor: Color,
|
||||
menuContainerColor: Color,
|
||||
isDark: Boolean,
|
||||
paletteStyle: String,
|
||||
): ThemeOverrideState {
|
||||
return remember(accentColor, menuBackgroundColor, menuContainerColor, isDark) {
|
||||
return remember(accentColor, menuBackgroundColor, menuContainerColor, isDark, paletteStyle) {
|
||||
buildReadThemeOverride(
|
||||
seedColor = accentColor,
|
||||
backgroundColor = null,
|
||||
containerColor = null,
|
||||
isDark = isDark,
|
||||
paletteStyle = paletteStyle,
|
||||
).let { base ->
|
||||
base.copy(
|
||||
colorScheme = base.colorScheme.withCustomReadMenuColors(
|
||||
@@ -176,6 +186,7 @@ private fun rememberReadThemeOverride(
|
||||
containerColor: Color?,
|
||||
fallbackDark: Boolean = LegadoTheme.isDark,
|
||||
deriveDarkFromColor: Boolean = true,
|
||||
paletteStyle: String = "",
|
||||
): ThemeOverrideState? {
|
||||
val isDark = remember(backgroundColor, containerColor, fallbackDark, deriveDarkFromColor) {
|
||||
if (deriveDarkFromColor) {
|
||||
@@ -184,12 +195,13 @@ private fun rememberReadThemeOverride(
|
||||
fallbackDark
|
||||
}
|
||||
}
|
||||
return remember(seedColor, backgroundColor, containerColor, isDark) {
|
||||
return remember(seedColor, backgroundColor, containerColor, isDark, paletteStyle) {
|
||||
buildReadThemeOverride(
|
||||
seedColor = seedColor,
|
||||
backgroundColor = backgroundColor,
|
||||
containerColor = containerColor,
|
||||
isDark = isDark,
|
||||
paletteStyle = paletteStyle,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -199,12 +211,17 @@ private fun buildReadThemeOverride(
|
||||
backgroundColor: Color?,
|
||||
containerColor: Color?,
|
||||
isDark: Boolean,
|
||||
paletteStyle: String = "",
|
||||
): ThemeOverrideState {
|
||||
val colorSpec = ThemeResolver.resolveColorSpecFromMaterialVersion(ThemeConfig.materialVersion)
|
||||
val resolvedPaletteStyle = paletteStyle
|
||||
.takeIf { it.isNotBlank() }
|
||||
?.let { ThemeResolver.resolvePaletteStyle(it) }
|
||||
?: ThemeResolver.resolvePaletteStyle(ThemeConfig.paletteStyle)
|
||||
val base = buildThemeOverrideState(
|
||||
seedColor = seedColor,
|
||||
isDark = isDark,
|
||||
paletteStyle = ThemeResolver.resolvePaletteStyle(ThemeConfig.paletteStyle),
|
||||
paletteStyle = resolvedPaletteStyle,
|
||||
colorSpec = colorSpec,
|
||||
usePureBlack = false,
|
||||
)
|
||||
|
||||
@@ -182,6 +182,8 @@ data class ReadMenuConfig(
|
||||
val readMenuBorderColor: Int = 0,
|
||||
val readMenuBorderColorNight: Int = 0,
|
||||
val readMenuBlurAlpha: Int = 60,
|
||||
val readMenuBlurColor: Int = 0,
|
||||
val readMenuPaletteStyle: String = "",
|
||||
val readMenuBlurRadius: Int = 24,
|
||||
val readMenuLensRadius: Float = 24f,
|
||||
val readMenuTopBarBlurMode: Int = ReadMenuBlurMode.None,
|
||||
@@ -482,6 +484,8 @@ sealed interface ReadBookIntent {
|
||||
data object OpenSystemTtsSettings : ReadBookIntent
|
||||
data object ClearTtsCache : ReadBookIntent
|
||||
data class SelectFont(val path: String) : ReadBookIntent
|
||||
data class SelectTitleFont(val path: String) : ReadBookIntent
|
||||
data class SelectTitleSystemTypeface(val index: Int) : ReadBookIntent
|
||||
data class SelectSystemTypeface(val index: Int) : ReadBookIntent
|
||||
data class ColorSelected(val dialogId: Int, val color: Int) : ReadBookIntent
|
||||
|
||||
@@ -653,6 +657,7 @@ sealed interface ReadBookSheet {
|
||||
data object ShadowSet : ReadBookSheet
|
||||
data object UnderlineConfig : ReadBookSheet
|
||||
data object FontSelect : ReadBookSheet
|
||||
data object TitleFontSelect : ReadBookSheet
|
||||
data object HighlightRuleConfig : ReadBookSheet
|
||||
data object MoreConfig : ReadBookSheet
|
||||
data object BgTextConfig : ReadBookSheet
|
||||
@@ -769,6 +774,18 @@ sealed interface ConfigUpdate {
|
||||
data class TitleColor(val color: Int) : ConfigUpdate {
|
||||
override val actions = setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.ReloadContent, ConfigUpdateAction.InvalidateTextPage)
|
||||
}
|
||||
data class TitleFont(val path: String) : ConfigUpdate {
|
||||
override val actions = setOf(ConfigUpdateAction.UpdateChapterStyle, ConfigUpdateAction.ReloadContent)
|
||||
}
|
||||
data class TitleSegType(val value: Int) : ConfigUpdate {
|
||||
override val actions = setOf(ConfigUpdateAction.UpdateChapterStyle, ConfigUpdateAction.ReloadContent)
|
||||
}
|
||||
data class TitleSegDistance(val value: Int) : ConfigUpdate {
|
||||
override val actions = setOf(ConfigUpdateAction.UpdateChapterStyle, ConfigUpdateAction.ReloadContent)
|
||||
}
|
||||
data class TitleSegFlag(val value: String) : ConfigUpdate {
|
||||
override val actions = setOf(ConfigUpdateAction.UpdateChapterStyle, ConfigUpdateAction.ReloadContent)
|
||||
}
|
||||
|
||||
// --- Header / footer tips ---
|
||||
data class HeaderMode(val value: Int) : ConfigUpdate {
|
||||
@@ -1023,6 +1040,12 @@ sealed interface ConfigUpdate {
|
||||
data class MenuBlurAlpha(val value: Int) : ConfigUpdate {
|
||||
override val actions = emptySet<ConfigUpdateAction>()
|
||||
}
|
||||
data class MenuBlurColor(val color: Int) : ConfigUpdate {
|
||||
override val actions = emptySet<ConfigUpdateAction>()
|
||||
}
|
||||
data class MenuPaletteStyle(val value: String) : ConfigUpdate {
|
||||
override val actions = emptySet<ConfigUpdateAction>()
|
||||
}
|
||||
data class MenuLensRadius(val value: Float) : ConfigUpdate {
|
||||
override val actions = emptySet<ConfigUpdateAction>()
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ fun ReadBookMenuBar(
|
||||
}
|
||||
}
|
||||
val hideTopBar = dialogLikeRoute ||
|
||||
currentRoute == ReadBookMenuRoute.ReadStyle && readStylePage >= 1
|
||||
currentRoute == ReadBookMenuRoute.TextTitle
|
||||
val menuColors = readMenuColors()
|
||||
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
@@ -683,6 +683,9 @@ private fun ReadBookMenuSurface(
|
||||
onOpenFontSelect = {
|
||||
onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.FontSelect))
|
||||
},
|
||||
onOpenTitleFontSelect = {
|
||||
onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.TitleFontSelect))
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
onIntent = onIntent,
|
||||
)
|
||||
@@ -902,55 +905,61 @@ private fun MenuTitleBar(
|
||||
)
|
||||
) {
|
||||
val useTitleCapsule = readMenuTopBarTitleCapsuleEnabled(backdrop, state.menuConfig)
|
||||
&& progressiveBlurActive
|
||||
val capsuleIconColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
|
||||
// Title row: back + title + actions + overflow
|
||||
// Title row: left group (back + capsule/title) + right group (actions)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
// Back button
|
||||
MenuTitleGlassButton(
|
||||
onClick = { onIntent(ReadBookIntent.CloseReadBook) },
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
state = state,
|
||||
colors = colors,
|
||||
backdrop = backdrop,
|
||||
)
|
||||
|
||||
if (useTitleCapsule) {
|
||||
// Title capsule layout: title + chapter in a glass pill
|
||||
TitleCapsuleGlassLayout(
|
||||
// Left group: back + capsule/title — fills remaining space
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MenuTitleGlassButton(
|
||||
onClick = { onIntent(ReadBookIntent.CloseReadBook) },
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
state = state,
|
||||
colors = colors,
|
||||
onIntent = onIntent,
|
||||
backdrop = backdrop,
|
||||
titleTextColor = titleTextColor,
|
||||
)
|
||||
} else if (titleBarMode != "1" && titleBarMode != "3") {
|
||||
AppText(
|
||||
text = state.bookName,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable { onIntent(ReadBookIntent.OpenBookInfo) }
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
style = LegadoTheme.typography.titleMedium.copy(
|
||||
shadow = androidx.compose.ui.graphics.Shadow(
|
||||
color = Color.Black.copy(alpha = 0.12f),
|
||||
offset = Offset.Zero,
|
||||
blurRadius = 12f
|
||||
)
|
||||
),
|
||||
color = titleTextColor,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
if (useTitleCapsule) {
|
||||
TitleCapsuleGlassLayout(
|
||||
state = state,
|
||||
colors = colors,
|
||||
onIntent = onIntent,
|
||||
backdrop = backdrop,
|
||||
titleTextColor = capsuleIconColor,
|
||||
)
|
||||
} else if (titleBarMode != "1" && titleBarMode != "3") {
|
||||
AppText(
|
||||
text = state.bookName,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable { onIntent(ReadBookIntent.OpenBookInfo) }
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
style = LegadoTheme.typography.titleMedium.copy(
|
||||
shadow = androidx.compose.ui.graphics.Shadow(
|
||||
color = Color.Black.copy(alpha = 0.12f),
|
||||
offset = Offset.Zero,
|
||||
blurRadius = 12f
|
||||
)
|
||||
),
|
||||
color = titleTextColor,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Right group: actions
|
||||
if (readMenuTopBarButtonLiquidGlassEnabled(backdrop, state.menuConfig)) {
|
||||
MenuTitleBarMergedGlassButton(
|
||||
state = state,
|
||||
@@ -963,7 +972,6 @@ private fun MenuTitleBar(
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
// Source action button (non-local books only)
|
||||
if (!state.isLocalBook) {
|
||||
SourceActionButton(
|
||||
state = state,
|
||||
@@ -1252,7 +1260,7 @@ private fun RowScope.TitleCapsuleGlassLayout(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(start = 12.dp)
|
||||
.height(40.dp)
|
||||
.readMenuLiquidGlass(
|
||||
backdrop = backdrop,
|
||||
@@ -1320,24 +1328,20 @@ private fun MenuTitleBarMergedGlassButton(
|
||||
val tint = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
|
||||
Box {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.size(48.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.readMenuLiquidGlass(
|
||||
backdrop = backdrop,
|
||||
colors = colors,
|
||||
shape = pillShape,
|
||||
useTopBarStyle = true,
|
||||
useLens = true,
|
||||
blurRadius = 32.dp,
|
||||
interactive = true,
|
||||
menuConfig = state.menuConfig,
|
||||
)
|
||||
.padding(horizontal = 4.dp),
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.readMenuLiquidGlass(
|
||||
backdrop = backdrop,
|
||||
colors = colors,
|
||||
shape = pillShape,
|
||||
useTopBarStyle = true,
|
||||
useLens = true,
|
||||
blurRadius = 32.dp,
|
||||
interactive = true,
|
||||
menuConfig = state.menuConfig,
|
||||
)
|
||||
.padding(horizontal = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
// SwapHoriz - change source
|
||||
@@ -1418,7 +1422,6 @@ private fun MenuTitleBarMergedGlassButton(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown menus
|
||||
if (!state.isLocalBook) {
|
||||
@@ -2827,8 +2830,9 @@ private fun Modifier.readMenuHazeEffect(
|
||||
progressiveBottomToTop: Boolean = false,
|
||||
): Modifier {
|
||||
val surfaceAlpha = menuConfig.readMenuBlurAlpha.coerceIn(0, 100) / 100f
|
||||
val blurTintColor = menuConfig.readMenuBlurColor.takeIf { it != 0 }?.let { Color(it) }
|
||||
val hazeContainerColor = if (progressive) {
|
||||
Color.Black.copy(alpha = surfaceAlpha)
|
||||
(blurTintColor ?: Color.Black).copy(alpha = surfaceAlpha)
|
||||
} else {
|
||||
colors.background.copy(alpha = surfaceAlpha)
|
||||
}
|
||||
|
||||
@@ -174,6 +174,17 @@ fun ReadBookScreen(
|
||||
onOpenFolderPicker = { onIntent(ReadBookIntent.OpenFontFolderPicker) },
|
||||
systemTypefaces = fontSelectSystemTypefaces,
|
||||
)
|
||||
FontSelectSheet(
|
||||
show = state.activeSheet is ReadBookSheet.TitleFontSelect,
|
||||
title = stringResource(R.string.read_config_title_settings),
|
||||
fontFolderUri = fontSelectFolderUri,
|
||||
selectedFontPath = ReadBookConfig.titleFont,
|
||||
onDismissRequest = dismissSheet,
|
||||
onSelectFont = { onIntent(ReadBookIntent.SelectTitleFont(it.uri.toString())) },
|
||||
onSelectSystemTypeface = { onIntent(ReadBookIntent.SelectTitleSystemTypeface(it)) },
|
||||
onOpenFolderPicker = { onIntent(ReadBookIntent.OpenFontFolderPicker) },
|
||||
systemTypefaces = fontSelectSystemTypefaces,
|
||||
)
|
||||
ToolButtonConfigSheet(
|
||||
show = state.activeSheet is ReadBookSheet.ToolButtonConfig,
|
||||
items = state.menuConfig.bottomBarButtons,
|
||||
|
||||
@@ -896,6 +896,14 @@ class ReadBookViewModel(
|
||||
}
|
||||
|
||||
is ReadBookIntent.SelectFont -> selectFont(intent.path)
|
||||
is ReadBookIntent.SelectTitleFont -> selectTitleFont(intent.path)
|
||||
is ReadBookIntent.SelectTitleSystemTypeface -> {
|
||||
ReadConfig.systemTypefaces = intent.index
|
||||
ReadBookConfig.titleFont = ""
|
||||
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
||||
setOf(ConfigUpdateAction.UpdateStyle, ConfigUpdateAction.ReloadContent)
|
||||
))
|
||||
}
|
||||
is ReadBookIntent.SelectSystemTypeface -> {
|
||||
ReadConfig.systemTypefaces = intent.index
|
||||
ReadBookConfig.textFont = ""
|
||||
@@ -1448,6 +1456,7 @@ class ReadBookViewModel(
|
||||
val old = previous
|
||||
previous = preferences
|
||||
ReadConfig.syncReadPreferences(preferences)
|
||||
ReadBookConfig.readMenuPaletteStyle = preferences.readMenuPaletteStyle
|
||||
_readPreferences.value = preferences
|
||||
if (!preferences.hasMenuClickArea()) {
|
||||
ReadConfig.detectClickArea()
|
||||
@@ -1599,6 +1608,8 @@ class ReadBookViewModel(
|
||||
readMenuBorderColor = ReadBookConfig.readMenuBorderColor,
|
||||
readMenuBorderColorNight = ReadBookConfig.readMenuBorderColorNight,
|
||||
readMenuBlurAlpha = ReadBookConfig.readMenuBlurAlpha,
|
||||
readMenuBlurColor = ReadBookConfig.readMenuBlurColor,
|
||||
readMenuPaletteStyle = ReadBookConfig.readMenuPaletteStyle,
|
||||
readMenuBlurRadius = ReadBookConfig.readMenuBlurRadius,
|
||||
readMenuLensRadius = ReadBookConfig.readMenuLensRadius,
|
||||
readMenuTopBarBlurMode = ReadBookConfig.readMenuTopBarBlurMode,
|
||||
@@ -2507,6 +2518,10 @@ class ReadBookViewModel(
|
||||
is ConfigUpdate.TitleTopSpacing -> ReadBookConfig.titleTopSpacing = update.value
|
||||
is ConfigUpdate.TitleBottomSpacing -> ReadBookConfig.titleBottomSpacing = update.value
|
||||
is ConfigUpdate.TitleColor -> ReadBookConfig.titleColor = update.color
|
||||
is ConfigUpdate.TitleFont -> ReadBookConfig.titleFont = update.path
|
||||
is ConfigUpdate.TitleSegType -> ReadBookConfig.titleSegType = update.value
|
||||
is ConfigUpdate.TitleSegDistance -> ReadBookConfig.titleSegDistance = update.value
|
||||
is ConfigUpdate.TitleSegFlag -> ReadBookConfig.titleSegFlag = update.value
|
||||
|
||||
// --- Header / footer tips ---
|
||||
is ConfigUpdate.HeaderMode -> ReadBookConfig.headerMode = update.value
|
||||
@@ -2829,6 +2844,20 @@ class ReadBookViewModel(
|
||||
}
|
||||
_uiState.update { it.copy(menuConfig = it.menuConfig.copy(readMenuBlurAlpha = update.value)) }
|
||||
}
|
||||
is ConfigUpdate.MenuBlurColor -> {
|
||||
ReadBookConfig.readMenuBlurColor = update.color
|
||||
viewModelScope.launch {
|
||||
readSettingsRepository.setReadMenuBlurColor(update.color)
|
||||
}
|
||||
_uiState.update { it.copy(menuConfig = it.menuConfig.copy(readMenuBlurColor = update.color)) }
|
||||
}
|
||||
is ConfigUpdate.MenuPaletteStyle -> {
|
||||
ReadBookConfig.readMenuPaletteStyle = update.value
|
||||
viewModelScope.launch {
|
||||
readSettingsRepository.setReadMenuPaletteStyle(update.value)
|
||||
}
|
||||
_uiState.update { it.copy(menuConfig = it.menuConfig.copy(readMenuPaletteStyle = update.value)) }
|
||||
}
|
||||
is ConfigUpdate.MenuLensRadius -> {
|
||||
ReadBookConfig.readMenuLensRadius = update.value
|
||||
viewModelScope.launch {
|
||||
@@ -3207,6 +3236,13 @@ class ReadBookViewModel(
|
||||
))
|
||||
}
|
||||
|
||||
private fun selectTitleFont(path: String) {
|
||||
ReadBookConfig.titleFont = path
|
||||
_effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
|
||||
setOf(ConfigUpdateAction.UpdateChapterStyle, ConfigUpdateAction.ReloadContent, ConfigUpdateAction.UpdateStyle)
|
||||
))
|
||||
}
|
||||
|
||||
private fun toggleDayNight() {
|
||||
val nextMode = if (ReadConfig.isNightTheme) "1" else "2"
|
||||
ThemeConfig.themeMode = nextMode
|
||||
|
||||
@@ -26,7 +26,11 @@ import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.SkipNext
|
||||
import androidx.compose.material.icons.filled.SkipPrevious
|
||||
import androidx.compose.material.icons.filled.Translate
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -40,13 +44,16 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -60,9 +67,11 @@ import io.legado.app.ui.book.read.ConfigUpdate
|
||||
import io.legado.app.ui.book.read.ReadBookButtonConfigItem
|
||||
import io.legado.app.ui.book.read.ReadBookIntent
|
||||
import io.legado.app.ui.book.read.ReadBookSheet
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.SectionTitle
|
||||
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
||||
import io.legado.app.ui.widget.components.settingItem.TinyClearColorModeSettingItem
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.TinyColorModeSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
|
||||
@@ -82,6 +91,7 @@ private const val COLOR_MENU_ACCENT_NIGHT = 9
|
||||
private const val COLOR_MENU_CONTAINER_NIGHT = 10
|
||||
private const val COLOR_BORDER = 11
|
||||
private const val COLOR_BORDER_NIGHT = 12
|
||||
private const val COLOR_BLUR_TINT = 13
|
||||
|
||||
@Composable
|
||||
internal fun SystemMenuPage(
|
||||
@@ -196,6 +206,7 @@ internal fun SystemMenuPage(
|
||||
COLOR_MENU_CONTAINER_NIGHT -> onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.MenuContainerColorNight(color)))
|
||||
COLOR_BORDER -> onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.BorderColor(color)))
|
||||
COLOR_BORDER_NIGHT -> onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.BorderColorNight(color)))
|
||||
COLOR_BLUR_TINT -> onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.MenuBlurColor(color)))
|
||||
}
|
||||
showColorPicker = false
|
||||
},
|
||||
@@ -264,6 +275,23 @@ private fun GlobalMenuTab(
|
||||
},
|
||||
)
|
||||
|
||||
val showPaletteStyle = bottomMode == 1 || (bottomMode == 2 && colorMode == 0)
|
||||
AnimatedVisibility(visible = showPaletteStyle) {
|
||||
val paletteEntries = stringArrayResource(R.array.paletteStyle)
|
||||
val paletteValues = stringArrayResource(R.array.paletteStyle_value)
|
||||
val allEntries = arrayOf(stringResource(R.string.flow_sys)) + paletteEntries
|
||||
val allValues = arrayOf("") + paletteValues
|
||||
TinyDropdownSettingItem(
|
||||
title = stringResource(R.string.palette_style),
|
||||
selectedValue = preferences.readMenuPaletteStyle,
|
||||
displayEntries = allEntries,
|
||||
entryValues = allValues,
|
||||
onValueChange = {
|
||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.MenuPaletteStyle(it)))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = bottomMode == 2) {
|
||||
Column {
|
||||
TinyDropdownSettingItem(
|
||||
@@ -461,6 +489,18 @@ private fun GlobalMenuTab(
|
||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.MenuBlurAlpha(it.toInt())))
|
||||
},
|
||||
)
|
||||
TinyClickableSettingItem(
|
||||
title = stringResource(R.string.read_menu_blur_color),
|
||||
onClick = {
|
||||
onShowColorPicker(COLOR_BLUR_TINT, preferences.readMenuBlurColor)
|
||||
},
|
||||
onLongClick = {
|
||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.MenuBlurColor(0)))
|
||||
},
|
||||
trailingContent = {
|
||||
TinyColorSwatch(color = preferences.readMenuBlurColor)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,3 +834,33 @@ internal fun readMenuButtonInfos(context: Context): List<ReadMenuButtonInfo> = l
|
||||
ReadMenuButtonInfo("replace_badge", Icons.Default.AutoAwesome, context.getString(R.string.replace_purify_badge)),
|
||||
ReadMenuButtonInfo("translate", Icons.Default.Translate, context.getString(R.string.translate)),
|
||||
)
|
||||
|
||||
// ========== Color Swatch ==========
|
||||
|
||||
@Composable
|
||||
private fun TinyColorSwatch(
|
||||
color: Int,
|
||||
) {
|
||||
val swatchColor = if (color != 0) Color(color) else LegadoTheme.colorScheme.onSurfaceVariant
|
||||
val borderColor = LegadoTheme.colorScheme.outline
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clip(CircleShape)
|
||||
.background(swatchColor)
|
||||
.then(
|
||||
if (color == 0) Modifier.border(1.5.dp, borderColor, CircleShape)
|
||||
else Modifier
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (color == 0) {
|
||||
AppText(
|
||||
text = "A",
|
||||
style = LegadoTheme.typography.labelSmall,
|
||||
color = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ fun ReadStyleTextTitleContent(
|
||||
onOpenUnderlineConfig: () -> Unit,
|
||||
onOpenHighlightRule: () -> Unit,
|
||||
onOpenFontSelect: () -> Unit,
|
||||
onOpenTitleFontSelect: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onIntent: (ReadBookIntent) -> Unit,
|
||||
) {
|
||||
@@ -85,6 +86,7 @@ fun ReadStyleTextTitleContent(
|
||||
onOpenUnderlineConfig = onOpenUnderlineConfig,
|
||||
onOpenHighlightRule = onOpenHighlightRule,
|
||||
onOpenFontSelect = onOpenFontSelect,
|
||||
onOpenTitleFontSelect = onOpenTitleFontSelect,
|
||||
animateToPage = { page -> scope.launch { pagerState.animateScrollToPage(page) } },
|
||||
modifier = modifier,
|
||||
onIntent = onIntent,
|
||||
@@ -101,6 +103,7 @@ internal fun ReadStyleTextTitleContent(
|
||||
onOpenUnderlineConfig: () -> Unit,
|
||||
onOpenHighlightRule: () -> Unit,
|
||||
onOpenFontSelect: () -> Unit,
|
||||
onOpenTitleFontSelect: () -> Unit,
|
||||
animateToPage: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onIntent: (ReadBookIntent) -> Unit,
|
||||
@@ -132,7 +135,10 @@ internal fun ReadStyleTextTitleContent(
|
||||
)
|
||||
|
||||
1 -> LayoutSpacingPage(onIntent = onIntent)
|
||||
2 -> TitleSettingsPage(onIntent = onIntent)
|
||||
2 -> TitleSettingsPage(
|
||||
onOpenTitleFontSelect = onOpenTitleFontSelect,
|
||||
onIntent = onIntent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,10 +364,14 @@ internal fun TextEffectsPage(
|
||||
|
||||
@Composable
|
||||
internal fun TitleSettingsPage(
|
||||
onOpenTitleFontSelect: () -> Unit,
|
||||
onIntent: (ReadBookIntent) -> Unit,
|
||||
) {
|
||||
var titleMode by remember(ReadBookConfig.titleMode) { mutableIntStateOf(ReadBookConfig.titleMode) }
|
||||
var titleBold by remember(ReadBookConfig.titleBold) { mutableIntStateOf(ReadBookConfig.titleBold) }
|
||||
var titleSegType by remember(ReadBookConfig.titleSegType) { mutableIntStateOf(ReadBookConfig.titleSegType) }
|
||||
var titleSegDistance by remember(ReadBookConfig.titleSegDistance) { mutableIntStateOf(ReadBookConfig.titleSegDistance) }
|
||||
var titleSegFlag by remember(ReadBookConfig.titleSegFlag) { mutableStateOf(ReadBookConfig.titleSegFlag) }
|
||||
var titleSegScaling by remember(ReadBookConfig.titleSegScaling) { mutableFloatStateOf(ReadBookConfig.titleSegScaling) }
|
||||
var titleLineSpacingExtra by remember(ReadBookConfig.titleLineSpacingExtra) { mutableIntStateOf(ReadBookConfig.titleLineSpacingExtra) }
|
||||
var titleLineSpacingSub by remember(ReadBookConfig.titleLineSpacingSub) { mutableIntStateOf(ReadBookConfig.titleLineSpacingSub) }
|
||||
@@ -432,6 +442,85 @@ internal fun TitleSettingsPage(
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// Title font
|
||||
TinyClickableSettingItem(
|
||||
title = stringResource(R.string.read_config_title_settings),
|
||||
imageVector = Icons.Default.TextFields,
|
||||
onClick = onOpenTitleFontSelect,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// Title segmentation
|
||||
TinyDropdownSettingItem(
|
||||
title = stringResource(R.string.split_title_mode),
|
||||
selectedValue = titleSegType.toString(),
|
||||
displayEntries = arrayOf(
|
||||
stringResource(R.string.close),
|
||||
stringResource(R.string.split_title_by_position),
|
||||
stringResource(R.string.split_title_by_flag),
|
||||
stringResource(R.string.split_title_by_regex),
|
||||
),
|
||||
entryValues = arrayOf("0", "1", "2", "3"),
|
||||
onValueChange = {
|
||||
titleSegType = it.toInt()
|
||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.TitleSegType(titleSegType)))
|
||||
},
|
||||
)
|
||||
if (titleSegType == 1) {
|
||||
TinySliderSettingItem(
|
||||
title = stringResource(R.string.split_title_position),
|
||||
value = titleSegDistance.toFloat(),
|
||||
valueRange = 1f..20f,
|
||||
steps = 18,
|
||||
onValueChange = { value ->
|
||||
titleSegDistance = value.toInt()
|
||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.TitleSegDistance(titleSegDistance)))
|
||||
},
|
||||
)
|
||||
}
|
||||
if (titleSegType == 2 || titleSegType == 3) {
|
||||
var showFlagDialog by remember { mutableStateOf(false) }
|
||||
|
||||
TinyClickableSettingItem(
|
||||
title = stringResource(R.string.rule_segment),
|
||||
description = titleSegFlag.ifBlank { stringResource(R.string.split_title_mode) },
|
||||
onClick = { showFlagDialog = true },
|
||||
)
|
||||
|
||||
if (showFlagDialog) {
|
||||
var flagText by remember { mutableStateOf(titleSegFlag) }
|
||||
androidx.compose.material3.AlertDialog(
|
||||
onDismissRequest = { showFlagDialog = false },
|
||||
title = { Text(stringResource(R.string.rule_segment)) },
|
||||
text = {
|
||||
androidx.compose.material3.OutlinedTextField(
|
||||
value = flagText,
|
||||
onValueChange = { flagText = it },
|
||||
singleLine = false,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
androidx.compose.material3.TextButton(onClick = {
|
||||
titleSegFlag = flagText
|
||||
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.TitleSegFlag(titleSegFlag)))
|
||||
showFlagDialog = false
|
||||
}) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
androidx.compose.material3.TextButton(onClick = { showFlagDialog = false }) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// Title spacing sliders
|
||||
TinySliderSettingItem(
|
||||
title = stringResource(R.string.subtitle_scale),
|
||||
|
||||
@@ -104,7 +104,7 @@ fun ScopeSelectSheet(
|
||||
applyDraftSelection()
|
||||
onDismissRequest()
|
||||
},
|
||||
text = stringResource(R.string.confirm),
|
||||
icon = AppIcons.Check,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,6 +92,7 @@ fun ColorScheme.toLegadoColorScheme(): LegadoColorScheme {
|
||||
fun ProvideColorSchemeOverride(
|
||||
colorScheme: ColorScheme,
|
||||
seedColor: Color = colorScheme.primary,
|
||||
overrideIsDark: Boolean? = null,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val themeAnimationSpec = tween<Color>(
|
||||
@@ -106,10 +107,12 @@ fun ProvideColorSchemeOverride(
|
||||
label = "theme_seed_animation"
|
||||
).value
|
||||
val legadoColorScheme = remember(animatedColorScheme) { animatedColorScheme.toLegadoColorScheme() }
|
||||
val overrideThemeMode = remember(baseThemeMode, animatedColorScheme, animatedSeedColor) {
|
||||
val resolvedIsDark = overrideIsDark ?: baseThemeMode.isDark
|
||||
val overrideThemeMode = remember(baseThemeMode, animatedColorScheme, animatedSeedColor, resolvedIsDark) {
|
||||
baseThemeMode.copy(
|
||||
colorScheme = animatedColorScheme,
|
||||
seedColor = animatedSeedColor,
|
||||
isDark = resolvedIsDark,
|
||||
)
|
||||
}
|
||||
val materialTypography = MaterialTheme.typography
|
||||
|
||||
@@ -16,6 +16,7 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
data class ThemeOverrideState(
|
||||
val seedColor: Color,
|
||||
val colorScheme: ColorScheme,
|
||||
val isDark: Boolean = false,
|
||||
)
|
||||
|
||||
fun buildThemeOverrideState(
|
||||
@@ -45,7 +46,8 @@ fun buildThemeOverrideState(
|
||||
|
||||
return ThemeOverrideState(
|
||||
seedColor = seedColor,
|
||||
colorScheme = colorScheme
|
||||
colorScheme = colorScheme,
|
||||
isDark = isDark,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,6 +74,7 @@ fun ProvideThemeOverride(
|
||||
ProvideColorSchemeOverride(
|
||||
colorScheme = currentTheme.colorScheme,
|
||||
seedColor = currentTheme.seedColor,
|
||||
overrideIsDark = currentTheme.isDark,
|
||||
content = content
|
||||
)
|
||||
} else {
|
||||
|
||||
+2
-2
@@ -438,8 +438,8 @@ fun ChangeSourceSheet(
|
||||
selectedSources = scopeState.sourceUrls,
|
||||
onToggleSource = { viewModel.toggleScopeSource(it) },
|
||||
isSourceScope = scopeState.isSource,
|
||||
onConfirm = {
|
||||
viewModel.startSearch()
|
||||
onApplyScope = { selection ->
|
||||
viewModel.applyScopeSelection(selection)
|
||||
showFilterSheet = false
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.LibraryBooks
|
||||
import androidx.compose.material.icons.automirrored.outlined.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
@@ -111,6 +112,10 @@ object AppIcons {
|
||||
@Composable
|
||||
get() = if (isMiuix) MiuixIcons.MoreCircle else Icons.Default.MoreHoriz
|
||||
|
||||
val Check: ImageVector
|
||||
@Composable
|
||||
get() = Icons.Default.Check
|
||||
|
||||
|
||||
@Composable
|
||||
fun mainDestination(destination: MainDestination, selected: Boolean): ImageVector {
|
||||
|
||||
@@ -65,6 +65,7 @@ fun RoundDropdownMenu(
|
||||
}
|
||||
} else {
|
||||
val colorScheme = rememberOpaqueColorScheme()
|
||||
val popupContainerColor = LegadoTheme.colorScheme.surfaceContainerLow
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
@@ -72,7 +73,7 @@ fun RoundDropdownMenu(
|
||||
modifier = modifier,
|
||||
shape = shape,
|
||||
shadowElevation = shadowElevation,
|
||||
containerColor = colorScheme.surfaceContainerLow
|
||||
containerColor = popupContainerColor
|
||||
) {
|
||||
MaterialExpressiveTheme(
|
||||
colorScheme = colorScheme,
|
||||
@@ -129,6 +130,7 @@ fun RoundDropdownMenuLazy(
|
||||
}
|
||||
} else {
|
||||
val colorScheme = rememberOpaqueColorScheme()
|
||||
val popupContainerColor = LegadoTheme.colorScheme.surfaceContainerLow
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
@@ -136,7 +138,7 @@ fun RoundDropdownMenuLazy(
|
||||
modifier = modifier,
|
||||
shape = shape,
|
||||
shadowElevation = shadowElevation,
|
||||
containerColor = colorScheme.surfaceContainerLow
|
||||
containerColor = popupContainerColor
|
||||
) {
|
||||
MaterialExpressiveTheme(
|
||||
colorScheme = colorScheme,
|
||||
|
||||
+4
-5
@@ -35,7 +35,6 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
||||
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||
import top.yukonga.miuix.kmp.basic.DropdownDefaults
|
||||
import top.yukonga.miuix.kmp.icon.MiuixIcons
|
||||
@@ -121,9 +120,9 @@ fun RoundDropdownMenuItem(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val colorScheme = rememberOpaqueColorScheme()
|
||||
val selectedContentColor = colorScheme.primary
|
||||
val defaultContentColor = colorScheme.onSurface
|
||||
val legadoColorScheme = LegadoTheme.colorScheme
|
||||
val selectedContentColor = legadoColorScheme.primary
|
||||
val defaultContentColor = legadoColorScheme.onSurface
|
||||
val contentColor = if (enabled) {
|
||||
when {
|
||||
hasCustomContentColor -> color
|
||||
@@ -137,7 +136,7 @@ fun RoundDropdownMenuItem(
|
||||
else -> defaultContentColor.copy(alpha = 0.38f)
|
||||
}
|
||||
}
|
||||
val containerColor = colorScheme.surface
|
||||
val containerColor = legadoColorScheme.surface
|
||||
|
||||
Surface(
|
||||
onClick = onClick,
|
||||
|
||||
Reference in New Issue
Block a user