فهرست منبع

refactor: 移除阅读 Sheet 与主题页面的配置门面依赖

将阴影、下划线、边距、标题和高亮规则等阅读 Sheet 所需配置汇总进 ReadBookUiState。

补齐书籍目录、默认书籍目录和电子墨水主题状态,使 Compose 页面只消费 ViewModel 提供的强类型状态。
5151561 3 ماه پیش
والد
کامیت
a8ebf99f08

+ 2 - 0
app/src/main/java/io/legado/app/ui/book/info/BookInfoContract.kt

@@ -46,6 +46,7 @@ data class BookInfoUiState(
     val loadCoverOnlyOnWifi: Boolean = false,
     val defaultCover: String = "",
     val defaultCoverDark: String = "",
+    val showMangaUi: Boolean = true,
 )
 
 @Stable
@@ -173,6 +174,7 @@ sealed interface BookInfoIntent {
 
     data class RelatedBookClick(val book: SearchBook) : BookInfoIntent
     data class RelatedBooksMore(val title: String, val url: String) : BookInfoIntent
+    data class SetDefaultBookTreeUri(val value: String) : BookInfoIntent
 }
 
 sealed interface BookInfoEffect {

+ 7 - 5
app/src/main/java/io/legado/app/ui/book/info/BookInfoRouteScreen.kt

@@ -11,6 +11,8 @@ import androidx.compose.animation.SharedTransitionScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.platform.LocalContext
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
@@ -26,8 +28,6 @@ import io.legado.app.ui.book.info.edit.BookInfoEditActivity
 import io.legado.app.ui.book.manga.ReadMangaActivity
 import io.legado.app.ui.book.source.edit.BookSourceEditActivity
 import io.legado.app.ui.book.toc.TocActivityResult
-import io.legado.app.ui.config.otherConfig.OtherConfig
-import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
 import io.legado.app.ui.file.HandleFileContract
 import io.legado.app.ui.login.SourceLoginActivity
 import io.legado.app.ui.widget.dialog.VariableDialog
@@ -63,13 +63,15 @@ fun BookInfoRouteScreen(
     val context = LocalContext.current
     val activity = context as AppCompatActivity
     val lifecycleOwner = LocalLifecycleOwner.current
+    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+    val showMangaUi by rememberUpdatedState(uiState.showMangaUi)
 
     val tocActivityResult = rememberLauncherForActivityResult(TocActivityResult()) {
         viewModel.onTocResult(it)
     }
     val localBookTreeSelect = rememberLauncherForActivityResult(HandleFileContract()) {
         it.uri?.let { treeUri ->
-            OtherConfig.defaultBookTreeUri = treeUri.toString()
+            viewModel.onIntent(BookInfoIntent.SetDefaultBookTreeUri(treeUri.toString()))
         }
     }
     val infoEditResult = rememberLauncherForActivityResult(
@@ -137,7 +139,7 @@ fun BookInfoRouteScreen(
                 is BookInfoEffect.OpenReader -> {
                     val cls = when {
                         effect.book.isAudio -> AudioPlayActivity::class.java
-                        !effect.book.isLocal && effect.book.isImage && ReadMangaConfig.showMangaUi -> {
+                        !effect.book.isLocal && effect.book.isImage && showMangaUi -> {
                             ReadMangaActivity::class.java
                         }
 
@@ -206,7 +208,7 @@ fun BookInfoRouteScreen(
     }
 
     BookInfoScreen(
-        state = viewModel.uiState.collectAsStateWithLifecycle().value,
+        state = uiState,
         groups = viewModel.allGroups
             .collectAsStateWithLifecycle(persistentListOf<BookGroup>()).value,
         onIntent = viewModel::onIntent,

+ 15 - 0
app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt

@@ -27,6 +27,8 @@ import io.legado.app.data.repository.RemoteBookRepository
 import io.legado.app.domain.usecase.ChangeBookSourceUseCase
 import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.domain.gateway.CoverSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsUpdate
 import io.legado.app.domain.usecase.ChangeSourceMigrationOptions
 import io.legado.app.domain.usecase.ClearBookCacheUseCase
 import io.legado.app.exception.NoBooksDirException
@@ -98,6 +100,7 @@ class BookInfoViewModel(
     private val imageLoader: ImageLoader,
     private val themeSettingsGateway: ThemeSettingsGateway,
     private val coverSettingsGateway: CoverSettingsGateway,
+    private val otherSettingsGateway: OtherSettingsGateway,
 ) : BaseViewModel(application) {
 
     val allGroups = bookGroupRepository.flowSelect().map { it.toImmutableList() }
@@ -111,6 +114,7 @@ class BookInfoViewModel(
     init {
         collectEventBus()
         collectAppearanceSettings()
+        collectOtherSettings()
     }
 
     private fun collectAppearanceSettings() {
@@ -134,6 +138,14 @@ class BookInfoViewModel(
         }
     }
 
+    private fun collectOtherSettings() {
+        viewModelScope.launch {
+            otherSettingsGateway.settings.collectLatest { settings ->
+                _uiState.update { it.copy(showMangaUi = settings.showMangaUi) }
+            }
+        }
+    }
+
     private fun collectEventBus() {
         viewModelScope.launch {
             eventFlow<Boolean>(EventBus.REFRESH_BOOK_INFO).collect {
@@ -354,6 +366,9 @@ class BookInfoViewModel(
 
             is BookInfoIntent.RelatedBookClick -> onRelatedBookClick(intent.book)
             is BookInfoIntent.RelatedBooksMore -> onRelatedBooksMore(intent.title, intent.url)
+            is BookInfoIntent.SetDefaultBookTreeUri -> viewModelScope.launch {
+                otherSettingsGateway.update(OtherSettingsUpdate.DefaultBookTreeUri(intent.value))
+            }
         }
     }
 

+ 55 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt

@@ -97,6 +97,60 @@ data class ReadBookStyleConfig(
     val isNightBgImage: Boolean get() = bgTypeNight != 0
 }
 
+@Stable
+data class ReadSheetConfigUiState(
+    val letterSpacing: Float = 0f,
+    val lineSpacing: Int = 0,
+    val paragraphSpacing: Int = 0,
+    val paragraphIndentCount: Int = 2,
+    val textItalic: Boolean = false,
+    val textBold: Int = 0,
+    val chineseConverterType: Int = 0,
+    val textColor: Int = 0,
+    val textAccentColor: Int = 0,
+    val titleMode: Int = 0,
+    val titleBold: Int = 0,
+    val titleSegType: Int = 0,
+    val titleSegDistance: Int = 0,
+    val titleSegFlag: String = "",
+    val titleSegScaling: Float = 1f,
+    val titleLineSpacingExtra: Int = 0,
+    val titleLineSpacingSub: Int = 0,
+    val titleSize: Int = 0,
+    val titleTopSpacing: Int = 0,
+    val titleBottomSpacing: Int = 0,
+    val titleColor: Int = 0,
+    val titleColorNight: Int = 0,
+    val textColorDay: Int = 0,
+    val textColorNight: Int = 0,
+    val textShadow: Boolean = false,
+    val textShadowColor: Int = 0,
+    val shadowRadius: Float = 0f,
+    val shadowDx: Float = 0f,
+    val shadowDy: Float = 0f,
+    val underline: Boolean = false,
+    val dottedLine: Boolean = false,
+    val underlineExtend: Boolean = false,
+    val underlineColor: Int = 0,
+    val underlineHeight: Int = 0,
+    val underlinePadding: Int = 0,
+    val dottedBase: Float = 0f,
+    val dottedRatio: Float = 0f,
+    val paddingTop: Int = 0,
+    val paddingBottom: Int = 0,
+    val paddingLeft: Int = 0,
+    val paddingRight: Int = 0,
+    val headerPaddingTop: Int = 0,
+    val headerPaddingBottom: Int = 0,
+    val headerPaddingLeft: Int = 0,
+    val headerPaddingRight: Int = 0,
+    val footerPaddingTop: Int = 0,
+    val footerPaddingBottom: Int = 0,
+    val footerPaddingLeft: Int = 0,
+    val footerPaddingRight: Int = 0,
+    val configNames: ImmutableList<String> = persistentListOf(),
+)
+
 @Stable
 data class ChapterSummaryUiState(
     val bookUrl: String = "",
@@ -250,6 +304,7 @@ data class ReadBookUiState(
     val readAloudParagraphInterval: Int = 0,
     // Style config (reactive state for ReadBookConfig)
     val styleConfig: ReadBookStyleConfig = ReadBookStyleConfig(),
+    val sheetConfig: ReadSheetConfigUiState = ReadSheetConfigUiState(),
     // Menu config (from ReadBookConfig via repository)
     val menuConfig: ReadMenuConfig = ReadMenuConfig(),
     val highlightRuleConfig: HighlightRuleConfigUiState = HighlightRuleConfigUiState(),

+ 2 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBar.kt

@@ -733,6 +733,7 @@ private fun ReadBookMenuSurface(
                             onBack = { onIntent(ReadBookIntent.ReadMenuBack) },
                         ) {
                             PaddingConfigContent(
+                                config = state.sheetConfig,
                                 onIntent = onIntent,
                                 modifier = Modifier.padding(horizontal = 16.dp),
                             )
@@ -763,6 +764,7 @@ private fun ReadBookMenuSurface(
                             onBack = { onIntent(ReadBookIntent.ReadMenuBack) },
                         ) {
                             ReadStyleTextTitleContent(
+                                config = state.sheetConfig,
                                 onOpenShadowSet = {
                                     onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.ShadowSet))
                                 },

+ 3 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt

@@ -157,6 +157,7 @@ fun ReadBookScreen(
 
     ShadowSetSheet(
         show = state.activeSheet is ReadBookSheet.ShadowSet,
+        config = state.sheetConfig,
         onDismissRequest = dismissSheet,
         onIntent = onIntent,
     )
@@ -189,6 +190,7 @@ fun ReadBookScreen(
     )
     UnderlineConfigSheet(
         show = state.activeSheet is ReadBookSheet.UnderlineConfig,
+        config = state.sheetConfig,
         onDismissRequest = dismissSheet,
         onIntent = onIntent,
     )
@@ -235,6 +237,7 @@ fun ReadBookScreen(
     HighlightRuleConfigSheet(
         show = state.activeSheet is ReadBookSheet.HighlightRuleConfig,
         state = state.highlightRuleConfig,
+        allConfigNames = state.sheetConfig.configNames,
         onDismissRequest = dismissSheet,
         onIntent = onIntent,
     )

+ 56 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -2041,6 +2041,61 @@ class ReadBookViewModel(
         )
     }
 
+    private fun buildSheetConfig(): ReadSheetConfigUiState = ReadSheetConfigUiState(
+        letterSpacing = ReadBookConfig.letterSpacing,
+        lineSpacing = ReadBookConfig.lineSpacingExtra,
+        paragraphSpacing = ReadBookConfig.paragraphSpacing,
+        paragraphIndentCount = ReadBookConfig.paragraphIndent.length,
+        textItalic = ReadBookConfig.textItalic,
+        textBold = ReadBookConfig.textBold,
+        chineseConverterType = ReadConfig.chineseConverterType,
+        textColor = ReadBookConfig.durConfig.curTextColor(),
+        textAccentColor = ReadBookConfig.durConfig.curTextAccentColor(),
+        titleMode = ReadBookConfig.titleMode,
+        titleBold = ReadBookConfig.titleBold,
+        titleSegType = ReadBookConfig.titleSegType,
+        titleSegDistance = ReadBookConfig.titleSegDistance,
+        titleSegFlag = ReadBookConfig.titleSegFlag,
+        titleSegScaling = ReadBookConfig.titleSegScaling,
+        titleLineSpacingExtra = ReadBookConfig.titleLineSpacingExtra,
+        titleLineSpacingSub = ReadBookConfig.titleLineSpacingSub,
+        titleSize = ReadBookConfig.titleSize,
+        titleTopSpacing = ReadBookConfig.titleTopSpacing,
+        titleBottomSpacing = ReadBookConfig.titleBottomSpacing,
+        titleColor = ReadBookConfig.titleColor,
+        titleColorNight = ReadBookConfig.titleColorNight,
+        textColorDay = ReadBookConfig.textColor,
+        textColorNight = ReadBookConfig.textColorNight,
+        textShadow = ReadBookConfig.textShadow,
+        textShadowColor = ReadBookConfig.durConfig.curTextShadowColor(),
+        shadowRadius = ReadBookConfig.shadowRadius,
+        shadowDx = ReadBookConfig.shadowDx,
+        shadowDy = ReadBookConfig.shadowDy,
+        underline = ReadBookConfig.underline,
+        dottedLine = ReadBookConfig.dottedLine,
+        underlineExtend = ReadBookConfig.underlineExtend,
+        underlineColor = ReadBookConfig.durConfig.curUnderlineColor(),
+        underlineHeight = ReadBookConfig.underlineHeight,
+        underlinePadding = ReadBookConfig.underlinePadding,
+        dottedBase = ReadBookConfig.durConfig.dottedBase,
+        dottedRatio = ReadBookConfig.durConfig.dottedRatio,
+        paddingTop = ReadBookConfig.paddingTop,
+        paddingBottom = ReadBookConfig.paddingBottom,
+        paddingLeft = ReadBookConfig.paddingLeft,
+        paddingRight = ReadBookConfig.paddingRight,
+        headerPaddingTop = ReadBookConfig.headerPaddingTop,
+        headerPaddingBottom = ReadBookConfig.headerPaddingBottom,
+        headerPaddingLeft = ReadBookConfig.headerPaddingLeft,
+        headerPaddingRight = ReadBookConfig.headerPaddingRight,
+        footerPaddingTop = ReadBookConfig.footerPaddingTop,
+        footerPaddingBottom = ReadBookConfig.footerPaddingBottom,
+        footerPaddingLeft = ReadBookConfig.footerPaddingLeft,
+        footerPaddingRight = ReadBookConfig.footerPaddingRight,
+        configNames = ReadBookConfig.configList.map { it.name }
+            .filter { it.isNotBlank() }
+            .toImmutableList(),
+    )
+
     private fun syncFromReadBook(current: ReadBookUiState): ReadBookUiState {
         val book = ReadBook.book
         val textChapter = ReadBook.curTextChapter
@@ -2074,6 +2129,7 @@ class ReadBookViewModel(
             sameTitleRemoved = textChapter?.sameTitleRemoved ?: false,
             isReadingProgressSyncConfigured = isReadingProgressSyncConfigured(),
             styleConfig = buildStyleConfig(),
+            sheetConfig = buildSheetConfig(),
             menuConfig = ReadMenuConfig(
                 titleBarIconPosition = ReadBookConfig.titleBarIconPosition,
                 showTitleBarIcons = ReadBookConfig.showTitleBarIcons,

+ 3 - 0
app/src/main/java/io/legado/app/ui/book/read/sheet/HighlightRuleConfigSheet.kt

@@ -51,6 +51,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState
 fun HighlightRuleConfigSheet(
     show: Boolean,
     state: HighlightRuleConfigUiState,
+    allConfigNames: List<String>,
     onDismissRequest: () -> Unit,
     onIntent: (ReadBookIntent) -> Unit,
 ) {
@@ -209,6 +210,7 @@ fun HighlightRuleConfigSheet(
     HighlightRuleEditSheet(
         show = show && editingRuleValue != null,
         rule = editingRuleValue,
+        allConfigNames = allConfigNames,
         onDismissRequest = { onIntent(ReadBookIntent.DismissHighlightRuleEdit) },
         onSave = { updated ->
             onIntent(ReadBookIntent.SaveHighlightRule(updated))
@@ -218,6 +220,7 @@ fun HighlightRuleConfigSheet(
     HighlightRuleEditSheet(
         show = show && state.showNewRule,
         rule = null,
+        allConfigNames = allConfigNames,
         onDismissRequest = { onIntent(ReadBookIntent.DismissHighlightRuleEdit) },
         onSave = { newRule ->
             onIntent(ReadBookIntent.SaveHighlightRule(newRule))

+ 1 - 2
app/src/main/java/io/legado/app/ui/book/read/sheet/HighlightRuleEditSheet.kt

@@ -48,7 +48,6 @@ import io.legado.app.data.entities.HighlightRule
 import io.legado.app.data.repository.ReadSettingsRepository
 import io.legado.app.data.repository.configNames
 import io.legado.app.data.repository.toJsonArray
-import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.AppTextField
 import io.legado.app.ui.widget.components.FontFolderState
@@ -74,6 +73,7 @@ import java.io.File
 fun HighlightRuleEditSheet(
     show: Boolean,
     rule: HighlightRule?,
+    allConfigNames: List<String>,
     onDismissRequest: () -> Unit,
     onSave: (HighlightRule) -> Unit,
 ) {
@@ -123,7 +123,6 @@ fun HighlightRuleEditSheet(
     var hasBgImage by remember(show, rule) { 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(show, rule) {
         mutableStateOf(initial.configName.orEmpty().configNames().toSet())
     }

+ 16 - 13
app/src/main/java/io/legado/app/ui/book/read/sheet/PaddingConfigSheet.kt

@@ -26,7 +26,7 @@ import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
-import io.legado.app.help.config.ReadBookConfig
+import io.legado.app.ui.book.read.ReadSheetConfigUiState
 import io.legado.app.ui.book.read.ConfigUpdate
 import io.legado.app.ui.book.read.ReadBookIntent
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
@@ -39,6 +39,7 @@ import kotlinx.coroutines.launch
 
 @Composable
 fun PaddingConfigSheet(
+    config: ReadSheetConfigUiState,
     onDismissRequest: () -> Unit,
     onIntent: (ReadBookIntent) -> Unit,
 ) {
@@ -51,6 +52,7 @@ fun PaddingConfigSheet(
         title = stringResource(R.string.padding),
     ) {
         PaddingConfigContent(
+            config = config,
             onIntent = onIntent,
             modifier = Modifier
                 .padding(bottom = 16.dp),
@@ -60,6 +62,7 @@ fun PaddingConfigSheet(
 
 @Composable
 fun PaddingConfigContent(
+    config: ReadSheetConfigUiState,
     onIntent: (ReadBookIntent) -> Unit,
     modifier: Modifier = Modifier,
 ) {
@@ -79,20 +82,20 @@ fun PaddingConfigContent(
     val pageHeights = remember { mutableStateMapOf<Int, Int>() }
     val animatedHeight by rememberPagerAnimatedHeight(pagerState, pageHeights)
 
-    var paddingTop by remember { mutableFloatStateOf(ReadBookConfig.paddingTop.toFloat()) }
-    var paddingBottom by remember { mutableFloatStateOf(ReadBookConfig.paddingBottom.toFloat()) }
-    var paddingLeft by remember { mutableFloatStateOf(ReadBookConfig.paddingLeft.toFloat()) }
-    var paddingRight by remember { mutableFloatStateOf(ReadBookConfig.paddingRight.toFloat()) }
+    var paddingTop by remember(config.paddingTop) { mutableFloatStateOf(config.paddingTop.toFloat()) }
+    var paddingBottom by remember(config.paddingBottom) { mutableFloatStateOf(config.paddingBottom.toFloat()) }
+    var paddingLeft by remember(config.paddingLeft) { mutableFloatStateOf(config.paddingLeft.toFloat()) }
+    var paddingRight by remember(config.paddingRight) { mutableFloatStateOf(config.paddingRight.toFloat()) }
 
-    var headerPaddingTop by remember { mutableFloatStateOf(ReadBookConfig.headerPaddingTop.toFloat()) }
-    var headerPaddingBottom by remember { mutableFloatStateOf(ReadBookConfig.headerPaddingBottom.toFloat()) }
-    var headerPaddingLeft by remember { mutableFloatStateOf(ReadBookConfig.headerPaddingLeft.toFloat()) }
-    var headerPaddingRight by remember { mutableFloatStateOf(ReadBookConfig.headerPaddingRight.toFloat()) }
+    var headerPaddingTop by remember(config.headerPaddingTop) { mutableFloatStateOf(config.headerPaddingTop.toFloat()) }
+    var headerPaddingBottom by remember(config.headerPaddingBottom) { mutableFloatStateOf(config.headerPaddingBottom.toFloat()) }
+    var headerPaddingLeft by remember(config.headerPaddingLeft) { mutableFloatStateOf(config.headerPaddingLeft.toFloat()) }
+    var headerPaddingRight by remember(config.headerPaddingRight) { mutableFloatStateOf(config.headerPaddingRight.toFloat()) }
 
-    var footerPaddingTop by remember { mutableFloatStateOf(ReadBookConfig.footerPaddingTop.toFloat()) }
-    var footerPaddingBottom by remember { mutableFloatStateOf(ReadBookConfig.footerPaddingBottom.toFloat()) }
-    var footerPaddingLeft by remember { mutableFloatStateOf(ReadBookConfig.footerPaddingLeft.toFloat()) }
-    var footerPaddingRight by remember { mutableFloatStateOf(ReadBookConfig.footerPaddingRight.toFloat()) }
+    var footerPaddingTop by remember(config.footerPaddingTop) { mutableFloatStateOf(config.footerPaddingTop.toFloat()) }
+    var footerPaddingBottom by remember(config.footerPaddingBottom) { mutableFloatStateOf(config.footerPaddingBottom.toFloat()) }
+    var footerPaddingLeft by remember(config.footerPaddingLeft) { mutableFloatStateOf(config.footerPaddingLeft.toFloat()) }
+    var footerPaddingRight by remember(config.footerPaddingRight) { mutableFloatStateOf(config.footerPaddingRight.toFloat()) }
 
     LaunchedEffect(pagerState) {
         snapshotFlow { pagerState.currentPage }.collect { page ->

+ 7 - 6
app/src/main/java/io/legado/app/ui/book/read/sheet/ShadowSetSheet.kt

@@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
-import io.legado.app.help.config.ReadBookConfig
+import io.legado.app.ui.book.read.ReadSheetConfigUiState
 import io.legado.app.ui.book.read.ConfigUpdate
 import io.legado.app.ui.book.read.ReadBookIntent
 import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
@@ -28,14 +28,15 @@ import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
 @Composable
 fun ShadowSetSheet(
     show: Boolean,
+    config: ReadSheetConfigUiState,
     onDismissRequest: () -> Unit,
     onIntent: (ReadBookIntent) -> Unit,
 ) {
-    var textShadow by remember { mutableStateOf(ReadBookConfig.textShadow) }
-    var shadowColor by remember { mutableIntStateOf(ReadBookConfig.durConfig.curTextShadowColor()) }
-    var shadowRadius by remember { mutableFloatStateOf(ReadBookConfig.shadowRadius) }
-    var shadowDx by remember { mutableFloatStateOf(ReadBookConfig.shadowDx) }
-    var shadowDy by remember { mutableFloatStateOf(ReadBookConfig.shadowDy) }
+    var textShadow by remember(show, config.textShadow) { mutableStateOf(config.textShadow) }
+    var shadowColor by remember(show, config.textShadowColor) { mutableIntStateOf(config.textShadowColor) }
+    var shadowRadius by remember(show, config.shadowRadius) { mutableFloatStateOf(config.shadowRadius) }
+    var shadowDx by remember(show, config.shadowDx) { mutableFloatStateOf(config.shadowDx) }
+    var shadowDy by remember(show, config.shadowDy) { mutableFloatStateOf(config.shadowDy) }
     var showColorPicker by remember { mutableStateOf(false) }
 
     AppModalBottomSheet(

+ 44 - 37
app/src/main/java/io/legado/app/ui/book/read/sheet/TextTitleSheet.kt

@@ -45,10 +45,9 @@ import androidx.compose.ui.res.stringArrayResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
-import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.ui.book.read.ConfigUpdate
 import io.legado.app.ui.book.read.ReadBookIntent
-import io.legado.app.ui.config.readConfig.ReadConfig
+import io.legado.app.ui.book.read.ReadSheetConfigUiState
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.AppSlider
 import io.legado.app.ui.widget.components.AppTextField
@@ -78,6 +77,7 @@ private const val COLOR_TITLE_NIGHT = 5
 
 @Composable
 fun ReadStyleTextTitleContent(
+    config: ReadSheetConfigUiState,
     onOpenShadowSet: () -> Unit,
     onOpenUnderlineConfig: () -> Unit,
     onOpenHighlightRule: () -> Unit,
@@ -105,6 +105,7 @@ fun ReadStyleTextTitleContent(
     }
 
     ReadStyleTextTitleContent(
+        config = config,
         tabTitles = tabTitles,
         selectedTab = selectedTab,
         pagerState = pagerState,
@@ -134,6 +135,7 @@ fun ReadStyleTextTitleContent(
 
 @Composable
 internal fun ReadStyleTextTitleContent(
+    config: ReadSheetConfigUiState,
     tabTitles: List<String>,
     selectedTab: Int,
     pagerState: PagerState,
@@ -183,6 +185,7 @@ internal fun ReadStyleTextTitleContent(
             ) {
                 when (page) {
                     0 -> TextEffectsPage(
+                        config = config,
                         onOpenShadowSet = onOpenShadowSet,
                         onOpenUnderlineConfig = onOpenUnderlineConfig,
                         onOpenHighlightRule = onOpenHighlightRule,
@@ -190,8 +193,9 @@ internal fun ReadStyleTextTitleContent(
                         onIntent = onIntent,
                     )
 
-                    1 -> LayoutSpacingPage(onIntent = onIntent)
+                    1 -> LayoutSpacingPage(config = config, onIntent = onIntent)
                     2 -> TitleSettingsPage(
+                        config = config,
                         onOpenTitleFontSelect = onOpenTitleFontSelect,
                         onIntent = onIntent,
                     )
@@ -205,13 +209,14 @@ internal fun ReadStyleTextTitleContent(
 
 @Composable
 internal fun LayoutSpacingPage(
+    config: ReadSheetConfigUiState,
     modifier: Modifier = Modifier,
     onIntent: (ReadBookIntent) -> Unit,
 ) {
-    var letterSpacing by remember { mutableFloatStateOf(ReadBookConfig.letterSpacing) }
-    var lineSpacing by remember { mutableFloatStateOf(ReadBookConfig.lineSpacingExtra.toFloat()) }
-    var paragraphSpacing by remember { mutableFloatStateOf(ReadBookConfig.paragraphSpacing.toFloat()) }
-    var indentCount by remember { mutableIntStateOf(ReadBookConfig.paragraphIndent.length) }
+    var letterSpacing by remember { mutableFloatStateOf(config.letterSpacing) }
+    var lineSpacing by remember { mutableFloatStateOf(config.lineSpacing.toFloat()) }
+    var paragraphSpacing by remember { mutableFloatStateOf(config.paragraphSpacing.toFloat()) }
+    var indentCount by remember { mutableIntStateOf(config.paragraphIndentCount) }
 
     Column(
         modifier = modifier
@@ -270,6 +275,7 @@ internal fun LayoutSpacingPage(
 
 @Composable
 internal fun TextEffectsPage(
+    config: ReadSheetConfigUiState,
     onOpenShadowSet: () -> Unit,
     onOpenUnderlineConfig: () -> Unit,
     onOpenHighlightRule: () -> Unit,
@@ -277,8 +283,8 @@ internal fun TextEffectsPage(
     modifier: Modifier = Modifier,
     onIntent: (ReadBookIntent) -> Unit,
 ) {
-    var textItalic by remember { mutableStateOf(ReadBookConfig.textItalic) }
-    var textBold by remember { mutableIntStateOf(ReadBookConfig.textBold) }
+    var textItalic by remember { mutableStateOf(config.textItalic) }
+    var textBold by remember { mutableIntStateOf(config.textBold) }
 
     var showColorPicker by remember { mutableStateOf(false) }
     var colorPickerId by remember { mutableIntStateOf(0) }
@@ -318,7 +324,7 @@ internal fun TextEffectsPage(
         val chineseConvertValues = remember { arrayOf("0", "1", "2") }
         TinyDropdownSettingItem(
             title = stringResource(R.string.chinese_converter),
-            selectedValue = ReadConfig.chineseConverterType.toString(),
+            selectedValue = config.chineseConverterType.toString(),
             displayEntries = chineseConvertEntries,
             entryValues = chineseConvertValues,
             onValueChange = {
@@ -330,19 +336,19 @@ internal fun TextEffectsPage(
         SectionTitle(stringResource(R.string.read_color))
         TinyColorSettingItem(
             title = stringResource(R.string.text_color),
-            colorValue = ReadBookConfig.durConfig.curTextColor(),
+            colorValue = config.textColor,
             onClick = {
                 colorPickerId = COLOR_TEXT
-                colorPickerInitial = ReadBookConfig.durConfig.curTextColor()
+                colorPickerInitial = config.textColor
                 showColorPicker = true
             },
         )
         TinyColorSettingItem(
             title = stringResource(R.string.text_accent_color),
-            colorValue = ReadBookConfig.durConfig.curTextAccentColor(),
+            colorValue = config.textAccentColor,
             onClick = {
                 colorPickerId = COLOR_ACCENT
-                colorPickerInitial = ReadBookConfig.durConfig.curTextAccentColor()
+                colorPickerInitial = config.textAccentColor
                 showColorPicker = true
             },
         )
@@ -394,21 +400,22 @@ internal fun TextEffectsPage(
 
 @Composable
 internal fun TitleSettingsPage(
+    config: ReadSheetConfigUiState,
     onOpenTitleFontSelect: () -> Unit,
     modifier: Modifier = Modifier,
     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) }
-    var titleSize by remember(ReadBookConfig.titleSize) { mutableIntStateOf(ReadBookConfig.titleSize) }
-    var titleTopSpacing by remember(ReadBookConfig.titleTopSpacing) { mutableIntStateOf(ReadBookConfig.titleTopSpacing) }
-    var titleBottomSpacing by remember(ReadBookConfig.titleBottomSpacing) { mutableIntStateOf(ReadBookConfig.titleBottomSpacing) }
+    var titleMode by remember(config.titleMode) { mutableIntStateOf(config.titleMode) }
+    var titleBold by remember(config.titleBold) { mutableIntStateOf(config.titleBold) }
+    var titleSegType by remember(config.titleSegType) { mutableIntStateOf(config.titleSegType) }
+    var titleSegDistance by remember(config.titleSegDistance) { mutableIntStateOf(config.titleSegDistance) }
+    var titleSegFlag by remember(config.titleSegFlag) { mutableStateOf(config.titleSegFlag) }
+    var titleSegScaling by remember(config.titleSegScaling) { mutableFloatStateOf(config.titleSegScaling) }
+    var titleLineSpacingExtra by remember(config.titleLineSpacingExtra) { mutableIntStateOf(config.titleLineSpacingExtra) }
+    var titleLineSpacingSub by remember(config.titleLineSpacingSub) { mutableIntStateOf(config.titleLineSpacingSub) }
+    var titleSize by remember(config.titleSize) { mutableIntStateOf(config.titleSize) }
+    var titleTopSpacing by remember(config.titleTopSpacing) { mutableIntStateOf(config.titleTopSpacing) }
+    var titleBottomSpacing by remember(config.titleBottomSpacing) { mutableIntStateOf(config.titleBottomSpacing) }
 
     var showColorPicker by remember { mutableStateOf(false) }
     var colorPickerId by remember { mutableIntStateOf(0) }
@@ -445,30 +452,30 @@ internal fun TitleSettingsPage(
 
         TinyColorModeSettingItem(
             title = stringResource(R.string.title_color),
-            dayColor = if (ReadBookConfig.titleColor != 0) {
-                ReadBookConfig.titleColor
+            dayColor = if (config.titleColor != 0) {
+                config.titleColor
             } else {
-                ReadBookConfig.textColor
+                config.textColorDay
             },
-            nightColor = if (ReadBookConfig.titleColorNight != 0) {
-                ReadBookConfig.titleColorNight
+            nightColor = if (config.titleColorNight != 0) {
+                config.titleColorNight
             } else {
-                ReadBookConfig.textColorNight
+                config.textColorNight
             },
             onClickColor = { isNight ->
                 if (isNight) {
                     colorPickerId = COLOR_TITLE_NIGHT
-                    colorPickerInitial = if (ReadBookConfig.titleColorNight != 0) {
-                        ReadBookConfig.titleColorNight
+                    colorPickerInitial = if (config.titleColorNight != 0) {
+                        config.titleColorNight
                     } else {
-                        ReadBookConfig.textColorNight
+                        config.textColorNight
                     }
                 } else {
                     colorPickerId = COLOR_TITLE
-                    colorPickerInitial = if (ReadBookConfig.titleColor != 0) {
-                        ReadBookConfig.titleColor
+                    colorPickerInitial = if (config.titleColor != 0) {
+                        config.titleColor
                     } else {
-                        ReadBookConfig.textColor
+                        config.textColorDay
                     }
                 }
                 showColorPicker = true

+ 10 - 9
app/src/main/java/io/legado/app/ui/book/read/sheet/UnderlineConfigSheet.kt

@@ -21,7 +21,7 @@ import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
-import io.legado.app.help.config.ReadBookConfig
+import io.legado.app.ui.book.read.ReadSheetConfigUiState
 import io.legado.app.ui.book.read.ConfigUpdate
 import io.legado.app.ui.book.read.ReadBookIntent
 import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
@@ -33,17 +33,18 @@ import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
 @Composable
 fun UnderlineConfigSheet(
     show: Boolean,
+    config: ReadSheetConfigUiState,
     onDismissRequest: () -> Unit,
     onIntent: (ReadBookIntent) -> Unit,
 ) {
-    var underline by remember { mutableStateOf(ReadBookConfig.underline) }
-    var dottedLine by remember { mutableStateOf(ReadBookConfig.dottedLine) }
-    var underlineExtend by remember { mutableStateOf(ReadBookConfig.underlineExtend) }
-    var underlineColor by remember { mutableStateOf(ReadBookConfig.durConfig.curUnderlineColor()) }
-    var underlineHeight by remember { mutableFloatStateOf(ReadBookConfig.underlineHeight.toFloat()) }
-    var underlinePadding by remember { mutableFloatStateOf(ReadBookConfig.underlinePadding.toFloat()) }
-    var dottedBase by remember { mutableFloatStateOf(ReadBookConfig.durConfig.dottedBase) }
-    var dottedRatio by remember { mutableFloatStateOf(ReadBookConfig.durConfig.dottedRatio) }
+    var underline by remember(show, config.underline) { mutableStateOf(config.underline) }
+    var dottedLine by remember(show, config.dottedLine) { mutableStateOf(config.dottedLine) }
+    var underlineExtend by remember(show, config.underlineExtend) { mutableStateOf(config.underlineExtend) }
+    var underlineColor by remember(show, config.underlineColor) { mutableStateOf(config.underlineColor) }
+    var underlineHeight by remember(show, config.underlineHeight) { mutableFloatStateOf(config.underlineHeight.toFloat()) }
+    var underlinePadding by remember(show, config.underlinePadding) { mutableFloatStateOf(config.underlinePadding.toFloat()) }
+    var dottedBase by remember(show, config.dottedBase) { mutableFloatStateOf(config.dottedBase) }
+    var dottedRatio by remember(show, config.dottedRatio) { mutableFloatStateOf(config.dottedRatio) }
     var showColorPicker by remember { mutableStateOf(false) }
 
     AppModalBottomSheet(

+ 1 - 0
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigContract.kt

@@ -13,6 +13,7 @@ data class ThemeConfigUiState(
     val theme: ThemeSettings = ThemeSettings(),
     val fontFolder: String = "",
     val activeSheet: ThemeConfigSheet? = null,
+    val showEInkTheme: Boolean = false,
 )
 
 sealed interface ThemeConfigSheet {

+ 1 - 2
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt

@@ -69,7 +69,6 @@ import io.legado.app.domain.gateway.ThemeFloatSetting
 import io.legado.app.domain.gateway.ThemeIntSetting
 import io.legado.app.domain.gateway.ThemeSettingsUpdate
 import io.legado.app.domain.gateway.ThemeStringSetting
-import io.legado.app.ui.config.labConfig.LabConfig
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeEngine
 import io.legado.app.ui.theme.ThemeResolver
@@ -246,7 +245,7 @@ fun ThemeConfigScreen(
                         Spacer(modifier = Modifier.height(16.dp))
 
                         val visibleThemes = themes.filter { (_, value) ->
-                            value != "4" || (LabConfig.labEnabled && LabConfig.eInkDisplay)
+                            value != "4" || state.showEInkTheme
                         }
                         ThemeColorSelector(
                             context = context,

+ 9 - 0
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigViewModel.kt

@@ -16,6 +16,7 @@ import io.legado.app.domain.gateway.ThemeIntSetting
 import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.domain.gateway.ThemeSettingsUpdate
 import io.legado.app.domain.gateway.ThemeStringSetting
+import io.legado.app.domain.gateway.LabSettingsGateway
 import io.legado.app.ui.main.MainDestination
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileUtils
@@ -38,6 +39,7 @@ class ThemeConfigViewModel(
     private val appShellSettingsGateway: AppShellSettingsGateway,
     private val readSettingsGateway: ReadSettingsGateway,
     private val themeSettingsGateway: ThemeSettingsGateway,
+    private val labSettingsGateway: LabSettingsGateway,
 ) : ViewModel() {
 
     private val _uiState = MutableStateFlow(
@@ -67,6 +69,13 @@ class ThemeConfigViewModel(
                 _uiState.update { it.copy(fontFolder = settings.fontFolder) }
             }
         }
+        viewModelScope.launch {
+            labSettingsGateway.settings.collect { settings ->
+                _uiState.update {
+                    it.copy(showEInkTheme = settings.enabled && settings.eInkDisplay)
+                }
+            }
+        }
     }
 
     fun onIntent(intent: ThemeConfigIntent) {