Ver código fonte

[优化] 增加大量 Compose 界面的模糊

HapeLee 8 meses atrás
pai
commit
27f602d9d4

+ 21 - 5
app/src/main/java/io/legado/app/base/BaseRuleViewModel.kt

@@ -44,11 +44,12 @@ sealed interface BaseRuleEvent {
 
 
 abstract class BaseRuleViewModel<T : SelectableItem<ID>, Entity, ID, S : ListUiState<T>>(
 abstract class BaseRuleViewModel<T : SelectableItem<ID>, Entity, ID, S : ListUiState<T>>(
     application: Application,
     application: Application,
-    initialState: S,
+    protected val initialState: S,
     private val uploadRepository: UploadRepository? = null // 设为可空,提高灵活性
     private val uploadRepository: UploadRepository? = null // 设为可空,提高灵活性
 ) : BaseViewModel(application) {
 ) : BaseViewModel(application) {
 
 
     protected val _searchKey = MutableStateFlow("")
     protected val _searchKey = MutableStateFlow("")
+    protected val _groupFilter = MutableStateFlow("")
     protected val _selectedIds = MutableStateFlow<Set<ID>>(emptySet())
     protected val _selectedIds = MutableStateFlow<Set<ID>>(emptySet())
     protected val _isSearchMode = MutableStateFlow(false)
     protected val _isSearchMode = MutableStateFlow(false)
     protected val _isUploading = MutableStateFlow(false)
     protected val _isUploading = MutableStateFlow(false)
@@ -60,24 +61,34 @@ abstract class BaseRuleViewModel<T : SelectableItem<ID>, Entity, ID, S : ListUiS
 
 
     abstract val rawDataFlow: Flow<List<Entity>>
     abstract val rawDataFlow: Flow<List<Entity>>
 
 
+    @Deprecated(
+        "Use filterData with groupFilter instead",
+        ReplaceWith("filterData(data, searchKey, \"\")")
+    )
     open fun filterData(data: List<Entity>, key: String): List<Entity> = data
     open fun filterData(data: List<Entity>, key: String): List<Entity> = data
 
 
+    open fun filterData(data: List<Entity>, searchKey: String, groupFilter: String): List<Entity> {
+        @Suppress("DEPRECATION")
+        return filterData(data, if (groupFilter.isNotEmpty()) groupFilter else searchKey)
+    }
+
     @OptIn(FlowPreview::class)
     @OptIn(FlowPreview::class)
     private val itemsFlow: Flow<List<T>> by lazy {
     private val itemsFlow: Flow<List<T>> by lazy {
         combine(
         combine(
             rawDataFlow,
             rawDataFlow,
             _searchKey,
             _searchKey,
+            _groupFilter,
             _localItems
             _localItems
-        ) { data, key, local ->
-            if (local != null && key.isEmpty()) {
+        ) { data, searchKey, groupFilter, local ->
+            if (local != null && searchKey.isEmpty() && groupFilter.isEmpty()) {
                 local
                 local
             } else {
             } else {
-                filterData(data, key).map { it.toUiItem() }
+                filterData(data, searchKey, groupFilter).map { it.toUiItem() }
             }
             }
         }
         }
     }
     }
 
 
-    val uiState: StateFlow<S> by lazy {
+    open val uiState: StateFlow<S> by lazy {
         combine(
         combine(
             itemsFlow,
             itemsFlow,
             _selectedIds,
             _selectedIds,
@@ -132,6 +143,11 @@ abstract class BaseRuleViewModel<T : SelectableItem<ID>, Entity, ID, S : ListUiS
         _searchKey.value = key ?: ""
         _searchKey.value = key ?: ""
     }
     }
 
 
+    open fun setGroupFilter(filter: String?) {
+        _localItems.value = null
+        _groupFilter.value = filter ?: ""
+    }
+
     fun setSearchMode(active: Boolean) {
     fun setSearchMode(active: Boolean) {
         _isSearchMode.value = active
         _isSearchMode.value = active
         if (!active) setSearchKey("")
         if (!active) setSearchKey("")

+ 4 - 0
app/src/main/java/io/legado/app/di/appModule.kt

@@ -18,11 +18,13 @@ import io.legado.app.ui.book.searchContent.SearchContentViewModel
 import io.legado.app.ui.book.toc.TocViewModel
 import io.legado.app.ui.book.toc.TocViewModel
 import io.legado.app.ui.book.toc.rule.TxtTocRuleViewModel
 import io.legado.app.ui.book.toc.rule.TxtTocRuleViewModel
 import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
 import io.legado.app.ui.config.otherConfig.OtherConfigViewModel
+import io.legado.app.ui.config.readConfig.ReadConfigViewModel
 import io.legado.app.ui.dict.rule.DictRuleViewModel
 import io.legado.app.ui.dict.rule.DictRuleViewModel
 import io.legado.app.ui.main.my.MyViewModel
 import io.legado.app.ui.main.my.MyViewModel
 import io.legado.app.ui.replace.ReplaceEditRoute
 import io.legado.app.ui.replace.ReplaceEditRoute
 import io.legado.app.ui.replace.ReplaceRuleViewModel
 import io.legado.app.ui.replace.ReplaceRuleViewModel
 import io.legado.app.ui.replace.edit.ReplaceEditViewModel
 import io.legado.app.ui.replace.edit.ReplaceEditViewModel
+import io.legado.app.ui.rss.source.manage.RssSourceViewModel
 import org.koin.core.module.dsl.singleOf
 import org.koin.core.module.dsl.singleOf
 import org.koin.core.module.dsl.viewModel
 import org.koin.core.module.dsl.viewModel
 import org.koin.core.module.dsl.viewModelOf
 import org.koin.core.module.dsl.viewModelOf
@@ -43,6 +45,7 @@ val appModule = module {
     single<ExploreRepository> { ExploreRepositoryImpl(get()) }
     single<ExploreRepository> { ExploreRepositoryImpl(get()) }
 
 
     viewModelOf(::DictRuleViewModel)
     viewModelOf(::DictRuleViewModel)
+    viewModelOf(::RssSourceViewModel)
     viewModelOf(::ReadRecordViewModel)
     viewModelOf(::ReadRecordViewModel)
     viewModelOf(::ExploreShowViewModel)
     viewModelOf(::ExploreShowViewModel)
     viewModelOf(::MyViewModel)
     viewModelOf(::MyViewModel)
@@ -50,6 +53,7 @@ val appModule = module {
     viewModelOf(::AllBookmarkViewModel)
     viewModelOf(::AllBookmarkViewModel)
     viewModelOf(::TxtTocRuleViewModel)
     viewModelOf(::TxtTocRuleViewModel)
     viewModelOf(::OtherConfigViewModel)
     viewModelOf(::OtherConfigViewModel)
+    viewModelOf(::ReadConfigViewModel)
     viewModelOf(::TocViewModel)
     viewModelOf(::TocViewModel)
     viewModelOf(::RemoteBookViewModel)
     viewModelOf(::RemoteBookViewModel)
     viewModelOf(::BookInfoViewModel)
     viewModelOf(::BookInfoViewModel)

+ 2 - 6
app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt

@@ -106,18 +106,14 @@ object ReadBookConfig {
         shareConfig = c ?: configList.getOrNull(5) ?: Config()
         shareConfig = c ?: configList.getOrNull(5) ?: Config()
     }
     }
 
 
-    fun upBg(width: Int, height: Int): Drawable? {
+    fun upBg(width: Int, height: Int) {
         val drawable = durConfig.curBgDrawable(width, height)
         val drawable = durConfig.curBgDrawable(width, height)
         if (drawable is BitmapDrawable && drawable.bitmap != null) {
         if (drawable is BitmapDrawable && drawable.bitmap != null) {
             bgMeanColor = drawable.bitmap.getMeanColor()
             bgMeanColor = drawable.bitmap.getMeanColor()
         } else if (drawable is ColorDrawable) {
         } else if (drawable is ColorDrawable) {
             bgMeanColor = drawable.color
             bgMeanColor = drawable.color
         }
         }
-        val tmp = bg
         bg = drawable
         bg = drawable
-        // 返回旧 Drawable,由调用方在视图更新完成后再 recycle,
-        // 避免视图仍引用旧 BitmapDrawable 时 bitmap 已被 recycle 导致崩溃
-        return tmp
     }
     }
 
 
     fun save() {
     fun save() {
@@ -1154,4 +1150,4 @@ object ReadBookConfig {
             return bgDrawable ?: fallbackColor.toDrawable()
             return bgDrawable ?: fallbackColor.toDrawable()
         }
         }
     }
     }
-}
+}

+ 7 - 4
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt

@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.lazy.rememberLazyListState
@@ -308,13 +307,17 @@ fun TxtRuleScreen(
                 })
                 })
         }
         }
     ) { paddingValues ->
     ) { paddingValues ->
-        Box(modifier = Modifier
+        Box(
+            modifier = Modifier
             .fillMaxSize()
             .fillMaxSize()
-            .padding(paddingValues)) {
+        ) {
             FastScrollLazyColumn(
             FastScrollLazyColumn(
                 modifier = Modifier.fillMaxSize(),
                 modifier = Modifier.fillMaxSize(),
                 state = listState,
                 state = listState,
-                contentPadding = PaddingValues(top = 8.dp, bottom = 120.dp),
+                contentPadding = PaddingValues(
+                    top = paddingValues.calculateTopPadding() + 8.dp,
+                    bottom = paddingValues.calculateBottomPadding() + 120.dp
+                ),
                 verticalArrangement = Arrangement.spacedBy(8.dp)
                 verticalArrangement = Arrangement.spacedBy(8.dp)
             ) {
             ) {
                 items(rules, key = { it.id }) { item ->
                 items(rules, key = { it.id }) { item ->

+ 7 - 4
app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt

@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.lazy.rememberLazyListState
@@ -304,13 +303,17 @@ fun DictRuleScreen(
                 })
                 })
         }
         }
     ) { paddingValues ->
     ) { paddingValues ->
-        Box(modifier = Modifier
+        Box(
+            modifier = Modifier
             .fillMaxSize()
             .fillMaxSize()
-            .padding(paddingValues)) {
+        ) {
             FastScrollLazyColumn(
             FastScrollLazyColumn(
                 modifier = Modifier.fillMaxSize(),
                 modifier = Modifier.fillMaxSize(),
                 state = listState,
                 state = listState,
-                contentPadding = PaddingValues(top = 8.dp, bottom = 120.dp),
+                contentPadding = PaddingValues(
+                    top = paddingValues.calculateTopPadding() + 8.dp,
+                    bottom = paddingValues.calculateBottomPadding() + 120.dp
+                ),
                 verticalArrangement = Arrangement.spacedBy(8.dp)
                 verticalArrangement = Arrangement.spacedBy(8.dp)
             ) {
             ) {
                 items(rules, key = { it.id }) { item ->
                 items(rules, key = { it.id }) { item ->

+ 9 - 6
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt

@@ -23,7 +23,6 @@ import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.FloatingActionButton
 import androidx.compose.material3.FloatingActionButton
-import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
 import androidx.compose.material3.LoadingIndicator
 import androidx.compose.material3.LoadingIndicator
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MaterialTheme
@@ -65,7 +64,10 @@ import io.legado.app.base.BaseRuleEvent
 import io.legado.app.data.entities.ReplaceRule
 import io.legado.app.data.entities.ReplaceRule
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.DraggableSelectionHandler
 import io.legado.app.ui.widget.components.DraggableSelectionHandler
+import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
+import io.legado.app.ui.widget.components.GroupManageBottomSheet
 import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
 import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
+import io.legado.app.ui.widget.components.divider.PillDivider
 import io.legado.app.ui.widget.components.exportComponents.FilePickerSheet
 import io.legado.app.ui.widget.components.exportComponents.FilePickerSheet
 import io.legado.app.ui.widget.components.exportComponents.FilePickerSheetMode
 import io.legado.app.ui.widget.components.exportComponents.FilePickerSheetMode
 import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
 import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
@@ -252,7 +254,8 @@ fun ReplaceRuleScreen(
         GroupManageBottomSheet(
         GroupManageBottomSheet(
             groups = groups,
             groups = groups,
             onDismissRequest = { showGroupManageSheet = false },
             onDismissRequest = { showGroupManageSheet = false },
-            viewModel = viewModel
+            onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
+            onDeleteGroup = { viewModel.delGroup(it) }
         )
         )
     }
     }
 
 
@@ -332,6 +335,7 @@ fun ReplaceRuleScreen(
                         .coerceAtLeast(0),
                         .coerceAtLeast(0),
                     edgePadding = 0.dp,
                     edgePadding = 0.dp,
                     divider = {},
                     divider = {},
+                    containerColor = GlassTopAppBarDefaults.containerColor(),
                 ) {
                 ) {
                     tabItems.forEachIndexed { index, title ->
                     tabItems.forEachIndexed { index, title ->
                         Tab(
                         Tab(
@@ -403,7 +407,7 @@ fun ReplaceRuleScreen(
                 text = { Text("帮助") },
                 text = { Text("帮助") },
                 onClick = { /*TODO*/ dismiss() }
                 onClick = { /*TODO*/ dismiss() }
             )
             )
-            HorizontalDivider()
+            PillDivider()
             RoundDropdownMenuItem(
             RoundDropdownMenuItem(
                 text = { Text("旧的在前") },
                 text = { Text("旧的在前") },
                 onClick = { viewModel.setSortMode("asc"); dismiss() }
                 onClick = { viewModel.setSortMode("asc"); dismiss() }
@@ -436,7 +440,6 @@ fun ReplaceRuleScreen(
     ) { padding ->
     ) { padding ->
         Box(
         Box(
             modifier = Modifier
             modifier = Modifier
-                .padding(padding)
                 .fillMaxSize()
                 .fillMaxSize()
         ) {
         ) {
             FastScrollLazyColumn(
             FastScrollLazyColumn(
@@ -444,8 +447,8 @@ fun ReplaceRuleScreen(
                 modifier = Modifier
                 modifier = Modifier
                     .fillMaxSize(),
                     .fillMaxSize(),
                 contentPadding = PaddingValues(
                 contentPadding = PaddingValues(
-                    top = 8.dp,
-                    bottom = 120.dp
+                    top = padding.calculateTopPadding() + 8.dp,
+                    bottom = padding.calculateBottomPadding() + 120.dp
                 ),
                 ),
                 verticalArrangement = Arrangement.spacedBy(8.dp)
                 verticalArrangement = Arrangement.spacedBy(8.dp)
             ) {
             ) {

+ 7 - 1
app/src/main/java/io/legado/app/ui/theme/HazeStyle.kt

@@ -30,9 +30,15 @@ fun Modifier.responsiveHazeEffect(
 
 
     if (!enableBlur) return this
     if (!enableBlur) return this
 
 
+    val style = if (enableProgressiveBlur) {
+        HazeMaterials.ultraThin()
+    } else {
+        HazeMaterials.regular()
+    }
+
     return this.hazeEffect(
     return this.hazeEffect(
         state = state,
         state = state,
-        style = HazeMaterials.ultraThin()
+        style = style
     ) {
     ) {
         if (enableProgressiveBlur) {
         if (enableProgressiveBlur) {
             progressive = HazeProgressive.verticalGradient(
             progressive = HazeProgressive.verticalGradient(

+ 2 - 0
app/src/main/java/io/legado/app/ui/widget/components/EmptyMessageView.kt

@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material3.ContainedLoadingIndicator
 import androidx.compose.material3.ContainedLoadingIndicator
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.mutableStateOf
@@ -69,6 +70,7 @@ fun EmptyMessageView(
 
 
         AnimatedTextLine(
         AnimatedTextLine(
             text = message,
             text = message,
+            style = MaterialTheme.typography.labelLarge,
             textAlign = TextAlign.Center,
             textAlign = TextAlign.Center,
             maxLines = 2,
             maxLines = 2,
             softWrap = true,
             softWrap = true,

+ 15 - 2
app/src/main/java/io/legado/app/ui/widget/components/GlassMediumFlexibleTopAppBar.kt

@@ -43,7 +43,7 @@ object GlassTopAppBarDefaults {
         val enableBlur = ThemeConfig.enableBlur
         val enableBlur = ThemeConfig.enableBlur
 
 
         val containerColor = if (enableBlur) {
         val containerColor = if (enableBlur) {
-            MaterialTheme.colorScheme.surface
+            MaterialTheme.colorScheme.surface.copy(alpha = 0f)
         } else {
         } else {
             MaterialTheme.colorScheme.surface.copy(alpha = alpha)
             MaterialTheme.colorScheme.surface.copy(alpha = alpha)
         }
         }
@@ -60,6 +60,19 @@ object GlassTopAppBarDefaults {
         )
         )
     }
     }
 
 
+    @Composable
+    fun containerColor(): Color {
+        val enableBlur = ThemeConfig.enableBlur
+
+        val containerColor = if (enableBlur) {
+            MaterialTheme.colorScheme.surface.copy(alpha = 0f)
+        } else {
+            MaterialTheme.colorScheme.surface
+        }
+
+        return containerColor
+    }
+
     @Composable
     @Composable
     fun controlContainerColor(): Color {
     fun controlContainerColor(): Color {
         val enableBlur = ThemeConfig.enableBlur
         val enableBlur = ThemeConfig.enableBlur
@@ -72,4 +85,4 @@ object GlassTopAppBarDefaults {
         }
         }
     }
     }
 
 
-}
+}

+ 31 - 18
app/src/main/java/io/legado/app/ui/widget/components/list/ListScaffold.kt

@@ -27,6 +27,7 @@ import androidx.compose.material3.TooltipAnchorPosition
 import androidx.compose.material3.TooltipBox
 import androidx.compose.material3.TooltipBox
 import androidx.compose.material3.TooltipDefaults
 import androidx.compose.material3.TooltipDefaults
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.material3.animateFloatingActionButton
 import androidx.compose.material3.animateFloatingActionButton
 import androidx.compose.material3.rememberTooltipState
 import androidx.compose.material3.rememberTooltipState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
@@ -36,6 +37,9 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import androidx.compose.ui.zIndex
+import dev.chrisbanes.haze.HazeState
+import io.legado.app.ui.theme.responsiveHazeEffect
+import io.legado.app.ui.theme.responsiveHazeSource
 import io.legado.app.ui.widget.components.SelectionActions
 import io.legado.app.ui.widget.components.SelectionActions
 import io.legado.app.ui.widget.components.SelectionBottomBar
 import io.legado.app.ui.widget.components.SelectionBottomBar
 import io.legado.app.ui.widget.components.rules.ListUiState
 import io.legado.app.ui.widget.components.rules.ListUiState
@@ -46,13 +50,14 @@ import io.legado.app.ui.widget.components.topbar.DynamicTopAppBar
 fun <T> ListScaffold(
 fun <T> ListScaffold(
     title: String,
     title: String,
     state: ListUiState<T>,
     state: ListUiState<T>,
-    onBackClick: () -> Unit,
+    subtitle: String? = null,
+    onBackClick: (() -> Unit)? = null,
     onSearchToggle: (Boolean) -> Unit,
     onSearchToggle: (Boolean) -> Unit,
     onSearchQueryChange: (String) -> Unit,
     onSearchQueryChange: (String) -> Unit,
     searchPlaceholder: String = "搜索...",
     searchPlaceholder: String = "搜索...",
     topBarActions: @Composable RowScope.() -> Unit = {},
     topBarActions: @Composable RowScope.() -> Unit = {},
-    bottomContent: @Composable (ColumnScope.() -> Unit)? = null,
-    dropDownMenuContent: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit = {},
+    bottomContent: @Composable (ColumnScope.(TopAppBarScrollBehavior) -> Unit)? = null,
+    dropDownMenuContent: @Composable (ColumnScope.(dismiss: () -> Unit) -> Unit)? = null,
     selectionActions: SelectionActions? = null,
     selectionActions: SelectionActions? = null,
     onAddClick: (() -> Unit)? = null,
     onAddClick: (() -> Unit)? = null,
     floatingActionButton: @Composable () -> Unit = {
     floatingActionButton: @Composable () -> Unit = {
@@ -80,28 +85,36 @@ fun <T> ListScaffold(
     content: @Composable (PaddingValues) -> Unit
     content: @Composable (PaddingValues) -> Unit
 ) {
 ) {
     val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
     val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
+    val hazeState = remember { HazeState() }
 
 
     Scaffold(
     Scaffold(
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
         snackbarHost = { SnackbarHost(snackbarHostState) },
         snackbarHost = { SnackbarHost(snackbarHostState) },
         topBar = {
         topBar = {
-            DynamicTopAppBar(
-                title = title,
-                state = state,
-                scrollBehavior = scrollBehavior,
-                onBackClick = onBackClick,
-                onSearchToggle = onSearchToggle,
-                onSearchQueryChange = onSearchQueryChange,
-                searchPlaceholder = searchPlaceholder,
-                onClearSelection = { selectionActions?.onSelectInvert?.invoke() }, // 或其他逻辑
-                topBarActions = topBarActions,
-                dropDownMenuContent = dropDownMenuContent,
-                bottomContent = bottomContent
-            )
+            Box(modifier = Modifier.responsiveHazeEffect(state = hazeState)) {
+                DynamicTopAppBar(
+                    title = title,
+                    subtitle = subtitle,
+                    state = state,
+                    scrollBehavior = scrollBehavior,
+                    onBackClick = onBackClick,
+                    onSearchToggle = onSearchToggle,
+                    onSearchQueryChange = onSearchQueryChange,
+                    searchPlaceholder = searchPlaceholder,
+                    onClearSelection = { selectionActions?.onSelectInvert?.invoke() },
+                    topBarActions = topBarActions,
+                    dropDownMenuContent = dropDownMenuContent,
+                    bottomContent = bottomContent
+                )
+            }
         },
         },
         floatingActionButton = floatingActionButton
         floatingActionButton = floatingActionButton
     ) { paddingValues ->
     ) { paddingValues ->
-        Box(modifier = Modifier.fillMaxSize()) {
+        Box(
+            modifier = Modifier
+                .fillMaxSize()
+                .responsiveHazeSource(hazeState)
+        ) {
             content(paddingValues)
             content(paddingValues)
 
 
             AnimatedVisibility(
             AnimatedVisibility(
@@ -124,4 +137,4 @@ fun <T> ListScaffold(
             }
             }
         }
         }
     }
     }
-}
+}

+ 84 - 11
app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenuItem.kt

@@ -1,31 +1,104 @@
 package io.legado.app.ui.widget.components.menuItem
 package io.legado.app.ui.widget.components.menuItem
 
 
-import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.LocalTextStyle
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MenuDefaults
+import androidx.compose.material3.Surface
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.rememberOpaqueColorScheme
 import io.legado.app.ui.theme.rememberOpaqueColorScheme
 
 
 @Composable
 @Composable
 fun RoundDropdownMenuItem(
 fun RoundDropdownMenuItem(
-    modifier: Modifier = Modifier,
     text: @Composable () -> Unit,
     text: @Composable () -> Unit,
     onClick: () -> Unit,
     onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    leadingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
+    enabled: Boolean = true,
+    contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
+    interactionSource: MutableInteractionSource? = null,
 ) {
 ) {
     val colorScheme = rememberOpaqueColorScheme()
     val colorScheme = rememberOpaqueColorScheme()
+    val interaction = interactionSource ?: remember { MutableInteractionSource() }
+
+    val contentColor =
+        if (enabled) MaterialTheme.colorScheme.onSurface
+        else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
 
 
-    DropdownMenuItem(
+    Surface(
+        onClick = onClick,
         modifier = modifier
         modifier = modifier
             .padding(horizontal = 8.dp)
             .padding(horizontal = 8.dp)
-            .clip(MaterialTheme.shapes.small)
-            .background(colorScheme.surface),
-        text = text,
-        trailingIcon = trailingIcon,
-        onClick = onClick
+            .fillMaxWidth(),
+        enabled = enabled,
+        shape = MaterialTheme.shapes.small,
+        color = colorScheme.surface,
+        interactionSource = interaction
+    ) {
+        CompositionLocalProvider(
+            LocalContentColor provides contentColor,
+            LocalTextStyle provides MaterialTheme.typography.labelLarge
+        ) {
+            Row(
+                modifier = Modifier
+                    .padding(contentPadding)
+                    .heightIn(min = 48.dp)
+                    .widthIn(min = 120.dp),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+
+                if (leadingIcon != null) {
+                    leadingIcon()
+                    Spacer(Modifier.width(8.dp))
+                }
+
+                Box(
+                    modifier = Modifier.weight(1f),
+                    contentAlignment = Alignment.CenterStart
+                ) {
+                    text()
+                }
+
+                if (trailingIcon != null) {
+                    Spacer(Modifier.width(8.dp))
+                    trailingIcon()
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun MenuItemIcon(
+    imageVector: ImageVector,
+    modifier: Modifier = Modifier,
+    contentDescription: String? = null,
+    tint: Color = LocalContentColor.current
+) {
+    Icon(
+        imageVector = imageVector,
+        contentDescription = contentDescription,
+        modifier = modifier.size(18.dp),
+        tint = tint
     )
     )
-}
+}

+ 31 - 67
app/src/main/java/io/legado/app/ui/widget/components/rules/RuleListScaffold.kt

@@ -1,17 +1,8 @@
 package io.legado.app.ui.widget.components.rules
 package io.legado.app.ui.widget.components.rules
 
 
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.slideInVertically
-import androidx.compose.animation.slideOutVertically
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.offset
-import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Delete
 import androidx.compose.material.icons.filled.Delete
@@ -20,20 +11,17 @@ import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.FloatingActionButton
 import androidx.compose.material3.FloatingActionButton
-import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.PlainTooltip
 import androidx.compose.material3.PlainTooltip
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SnackbarHost
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.Text
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.TooltipAnchorPosition
 import androidx.compose.material3.TooltipAnchorPosition
 import androidx.compose.material3.TooltipBox
 import androidx.compose.material3.TooltipBox
 import androidx.compose.material3.TooltipDefaults
 import androidx.compose.material3.TooltipDefaults
-import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.material3.animateFloatingActionButton
 import androidx.compose.material3.animateFloatingActionButton
 import androidx.compose.material3.rememberTooltipState
 import androidx.compose.material3.rememberTooltipState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
@@ -43,27 +31,25 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.zIndex
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.ActionItem
-import io.legado.app.ui.widget.components.SelectionBottomBar
-import io.legado.app.ui.widget.components.topbar.DynamicTopAppBar
+import io.legado.app.ui.widget.components.SelectionActions
+import io.legado.app.ui.widget.components.list.ListScaffold
 
 
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 @Composable
 fun <T> RuleListScaffold(
 fun <T> RuleListScaffold(
     title: String,
     title: String,
     state: ListUiState<T>,
     state: ListUiState<T>,
+    subtitle: String? = null,
     onBackClick: () -> Unit,
     onBackClick: () -> Unit,
     onSearchToggle: (Boolean) -> Unit,
     onSearchToggle: (Boolean) -> Unit,
     onSearchQueryChange: (String) -> Unit,
     onSearchQueryChange: (String) -> Unit,
     searchPlaceholder: String = "搜索...",
     searchPlaceholder: String = "搜索...",
     topBarActions: @Composable RowScope.() -> Unit = {},
     topBarActions: @Composable RowScope.() -> Unit = {},
-    bottomContent: @Composable (ColumnScope.() -> Unit)? = null,
-    dropDownMenuContent: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit = {},
+    bottomContent: @Composable (ColumnScope.(TopAppBarScrollBehavior) -> Unit)? = null,
+    dropDownMenuContent: (@Composable ColumnScope.(dismiss: () -> Unit) -> Unit)? = null,
     onClearSelection: () -> Unit,
     onClearSelection: () -> Unit,
     onSelectAll: () -> Unit,
     onSelectAll: () -> Unit,
     onSelectInvert: () -> Unit,
     onSelectInvert: () -> Unit,
@@ -94,8 +80,6 @@ fun <T> RuleListScaffold(
     snackbarHostState: SnackbarHostState,
     snackbarHostState: SnackbarHostState,
     content: @Composable (PaddingValues) -> Unit
     content: @Composable (PaddingValues) -> Unit
 ) {
 ) {
-    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
-    var showMenu by remember { mutableStateOf(false) }
     var showDeleteConfirmDialog by remember { mutableStateOf(false) }
     var showDeleteConfirmDialog by remember { mutableStateOf(false) }
 
 
     if (showDeleteConfirmDialog) {
     if (showDeleteConfirmDialog) {
@@ -124,50 +108,30 @@ fun <T> RuleListScaffold(
         )
         )
     }
     }
 
 
-    Scaffold(
-        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
-        snackbarHost = { SnackbarHost(snackbarHostState) },
-        topBar = {
-            DynamicTopAppBar(
-                title = title,
-                state = state,
-                scrollBehavior = scrollBehavior,
-                onBackClick = onBackClick,
-                onSearchToggle = onSearchToggle,
-                onSearchQueryChange = onSearchQueryChange,
-                searchPlaceholder = searchPlaceholder,
-                onClearSelection = onClearSelection,
-                topBarActions = topBarActions,
-                dropDownMenuContent = dropDownMenuContent,
-                bottomContent = bottomContent
-            )
-        },
+    ListScaffold(
+        title = title,
+        subtitle = subtitle,
+        state = state,
+        onBackClick = onBackClick,
+        onSearchToggle = onSearchToggle,
+        onSearchQueryChange = onSearchQueryChange,
+        searchPlaceholder = searchPlaceholder,
+        topBarActions = topBarActions,
+        bottomContent = bottomContent,
+        dropDownMenuContent = dropDownMenuContent,
+        onAddClick = onAddClick,
         floatingActionButton = floatingActionButton,
         floatingActionButton = floatingActionButton,
-        content = { paddingValues ->
-            Box(modifier = Modifier.fillMaxSize()) {
-                content(paddingValues)
-                AnimatedVisibility(
-                    visible = state.selectedIds.isNotEmpty(),
-                    modifier = Modifier
-                        .align(Alignment.BottomCenter)
-                        .offset(y = -ScreenOffset)
-                        .padding(bottom = 16.dp)
-                        .zIndex(1f),
-                    enter = slideInVertically { it } + fadeIn(),
-                    exit = slideOutVertically { it } + fadeOut()
-                ) {
-                    SelectionBottomBar(
-                        onSelectAll = onSelectAll,
-                        onSelectInvert = onSelectInvert,
-                        primaryAction = ActionItem(
-                            text = stringResource(R.string.delete),
-                            icon = { Icon(Icons.Default.Delete, null) },
-                            onClick = { showDeleteConfirmDialog = true }
-                        ),
-                        secondaryActions = selectionSecondaryActions
-                    )
-                }
-            }
-        }
+        snackbarHostState = snackbarHostState,
+        selectionActions = SelectionActions(
+            onSelectAll = onSelectAll,
+            onSelectInvert = onSelectInvert,
+            primaryAction = ActionItem(
+                text = stringResource(R.string.delete),
+                icon = { Icon(Icons.Default.Delete, null) },
+                onClick = { showDeleteConfirmDialog = true }
+            ),
+            secondaryActions = selectionSecondaryActions
+        ),
+        content = content
     )
     )
-}
+}

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/components/settingItem/ListSettingItem.kt

@@ -3,13 +3,13 @@ package io.legado.app.ui.widget.components.settingItem
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Check
 import androidx.compose.material.icons.filled.Check
-import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Text
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
+import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 
 
 @Composable
 @Composable
 fun DropdownListSettingItem(
 fun DropdownListSettingItem(
@@ -31,7 +31,7 @@ fun DropdownListSettingItem(
         onClick = { },
         onClick = { },
         dropdownMenu = { onDismiss ->
         dropdownMenu = { onDismiss ->
             displayEntries.forEachIndexed { index, display ->
             displayEntries.forEachIndexed { index, display ->
-                DropdownMenuItem(
+                RoundDropdownMenuItem(
                     text = { Text(display) },
                     text = { Text(display) },
                     onClick = {
                     onClick = {
                         onValueChange(entryValues[index])
                         onValueChange(entryValues[index])

+ 8 - 7
app/src/main/java/io/legado/app/ui/widget/components/settingItem/SettingItem.kt

@@ -11,10 +11,8 @@ import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.KeyboardArrowDown
 import androidx.compose.material.icons.filled.KeyboardArrowDown
-import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
 import androidx.compose.material3.ListItem
 import androidx.compose.material3.ListItem
@@ -30,10 +28,12 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.rotate
 import androidx.compose.ui.draw.rotate
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.card.GlassCard
+import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 @Composable
@@ -42,6 +42,7 @@ fun SettingItem(
     painter: Painter? = null,
     painter: Painter? = null,
     imageVector: ImageVector? = null,
     imageVector: ImageVector? = null,
     color: Color? = null,
     color: Color? = null,
+    shape: Shape = MaterialTheme.shapes.extraSmall,
     title: String,
     title: String,
     description: String? = null,
     description: String? = null,
     option: String? = null,
     option: String? = null,
@@ -58,14 +59,14 @@ fun SettingItem(
     val isExpandable = expandContent != null && onExpandChange != null
     val isExpandable = expandContent != null && onExpandChange != null
 
 
     GlassCard(
     GlassCard(
-        modifier = Modifier
+        modifier = modifier
             .fillMaxWidth(),
             .fillMaxWidth(),
-        shape = RoundedCornerShape(4.dp),
+        shape = shape,
         color = color ?: MaterialTheme.colorScheme.surfaceContainerLow,
         color = color ?: MaterialTheme.colorScheme.surfaceContainerLow,
     ) {
     ) {
         Column {
         Column {
             ListItem(
             ListItem(
-                modifier = modifier
+                modifier = Modifier
                     .combinedClickable(
                     .combinedClickable(
                         onClick = {
                         onClick = {
                             when {
                             when {
@@ -139,7 +140,7 @@ fun SettingItem(
                         }
                         }
 
 
                         dropdownMenu?.let { menu ->
                         dropdownMenu?.let { menu ->
-                            DropdownMenu(
+                            RoundDropdownMenu(
                                 expanded = showMenu,
                                 expanded = showMenu,
                                 onDismissRequest = { showMenu = false }) {
                                 onDismissRequest = { showMenu = false }) {
                                 menu { showMenu = false }
                                 menu { showMenu = false }
@@ -167,4 +168,4 @@ fun SettingItem(
             }
             }
         }
         }
     }
     }
-}
+}

+ 22 - 16
app/src/main/java/io/legado/app/ui/widget/components/topbar/DynamicTopAppBar.kt

@@ -16,7 +16,6 @@ import androidx.compose.material.icons.filled.MoreVert
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.getValue
@@ -29,6 +28,7 @@ import io.legado.app.ui.widget.components.AdaptiveAnimatedText
 import io.legado.app.ui.widget.components.AnimatedTextLine
 import io.legado.app.ui.widget.components.AnimatedTextLine
 import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
 import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
 import io.legado.app.ui.widget.components.SearchBarSection
 import io.legado.app.ui.widget.components.SearchBarSection
+import io.legado.app.ui.widget.components.button.TopBarActionButton
 import io.legado.app.ui.widget.components.button.TopbarNavigationButton
 import io.legado.app.ui.widget.components.button.TopbarNavigationButton
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.rules.ListUiState
 import io.legado.app.ui.widget.components.rules.ListUiState
@@ -41,7 +41,7 @@ fun <T> DynamicTopAppBar(
     subtitle: String? = null,
     subtitle: String? = null,
     state: ListUiState<T>,
     state: ListUiState<T>,
     scrollBehavior: TopAppBarScrollBehavior,
     scrollBehavior: TopAppBarScrollBehavior,
-    onBackClick: () -> Unit,
+    onBackClick: (() -> Unit)? = null,
     onSearchToggle: (Boolean) -> Unit,
     onSearchToggle: (Boolean) -> Unit,
     onSearchQueryChange: (String) -> Unit,
     onSearchQueryChange: (String) -> Unit,
     searchPlaceholder: String,
     searchPlaceholder: String,
@@ -51,7 +51,7 @@ fun <T> DynamicTopAppBar(
     onClearSelection: () -> Unit,
     onClearSelection: () -> Unit,
     topBarActions: @Composable RowScope.() -> Unit = {},
     topBarActions: @Composable RowScope.() -> Unit = {},
     dropDownMenuContent: @Composable (ColumnScope.(dismiss: () -> Unit) -> Unit)? = null,
     dropDownMenuContent: @Composable (ColumnScope.(dismiss: () -> Unit) -> Unit)? = null,
-    bottomContent: @Composable (ColumnScope.() -> Unit)? = null
+    bottomContent: @Composable (ColumnScope.(TopAppBarScrollBehavior) -> Unit)? = null
 ) {
 ) {
     var showMenu by remember { mutableStateOf(false) }
     var showMenu by remember { mutableStateOf(false) }
     val isSelecting = state.selectedIds.isNotEmpty()
     val isSelecting = state.selectedIds.isNotEmpty()
@@ -75,25 +75,31 @@ fun <T> DynamicTopAppBar(
                 { AnimatedTextLine(text = it) }
                 { AnimatedTextLine(text = it) }
             },
             },
             navigationIcon = {
             navigationIcon = {
-                TopbarNavigationButton(
-                    onClick = { if (isSelecting) onClearSelection() else onBackClick() },
-                    imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
-                    contentDescription = if (isSelecting) "取消选择" else "返回"
-                )
+                if (isSelecting || onBackClick != null) {
+                    TopbarNavigationButton(
+                        onClick = { if (isSelecting) onClearSelection() else onBackClick?.invoke() },
+                        imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
+                        contentDescription = if (isSelecting) "取消选择" else "返回"
+                    )
+                }
             },
             },
             actions = {
             actions = {
                 if (!isSelecting) {
                 if (!isSelecting) {
-                    IconButton(onClick = { onSearchToggle(!state.isSearch) }) {
-                        Icon(Icons.Default.Search, null)
-                    }
+                    TopBarActionButton(
+                        onClick = { onSearchToggle(!state.isSearch) },
+                        imageVector = Icons.Default.Search,
+                        contentDescription = "搜索"
+                    )
 
 
                     topBarActions()
                     topBarActions()
 
 
                     dropDownMenuContent?.let { content ->
                     dropDownMenuContent?.let { content ->
                         Box {
                         Box {
-                            IconButton(onClick = { showMenu = true }) {
-                                Icon(Icons.Default.MoreVert, null)
-                            }
+                            TopBarActionButton(
+                                onClick = { showMenu = true },
+                                imageVector = Icons.Default.MoreVert,
+                                contentDescription = "更多"
+                            )
                             RoundDropdownMenu(
                             RoundDropdownMenu(
                                 expanded = showMenu,
                                 expanded = showMenu,
                                 onDismissRequest = { showMenu = false }
                                 onDismissRequest = { showMenu = false }
@@ -122,6 +128,6 @@ fun <T> DynamicTopAppBar(
             )
             )
         }
         }
 
 
-        bottomContent?.invoke(this)
+        bottomContent?.invoke(this, scrollBehavior)
     }
     }
-}
+}

+ 1 - 1
app/src/main/res/values-zh-rCN/strings.xml

@@ -755,7 +755,7 @@
     <string name="default_cover">默认封面</string>
     <string name="default_cover">默认封面</string>
     <string name="restore_ignore">恢复忽略列表</string>
     <string name="restore_ignore">恢复忽略列表</string>
     <string name="restore_ignore_summary">选择恢复忽略内容</string>
     <string name="restore_ignore_summary">选择恢复忽略内容</string>
-    <string name="read_config">界面设置</string>
+    <string name="read_config">阅读界面</string>
     <string name="group_name">分组名称</string>
     <string name="group_name">分组名称</string>
     <string name="note_content">备注</string>
     <string name="note_content">备注</string>
     <string name="replace_enable_default_t">默认启用替换净化</string>
     <string name="replace_enable_default_t">默认启用替换净化</string>