Просмотр исходного кода

[修复] 修复分组管理的抽动问题
[优化] 布局模式为网格时,添加仅封面样式

HapeLee 7 месяцев назад
Родитель
Сommit
ab907f8062

+ 1 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -52,6 +52,7 @@ object PreferKey {
     const val bookshelfLayoutModePortrait = "bookshelfLayoutPortrait"
     const val bookshelfLayoutModeLandscape = "bookshelf_layout_landscape"
     const val bookshelfLayoutCompact = "bookshelfLayoutCompact"
+    const val bookshelfGridLayout = "bookshelfGridLayout"
     const val bookshelfSort = "bookshelfSort"
     const val bookExportFileName = "bookExportFileName"
     const val bookImportFileName = "bookImportFileName"

+ 5 - 0
app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt

@@ -88,6 +88,11 @@ object BookshelfConfig {
      */
     var bookshelfLayoutGridLandscape by prefDelegate(PreferKey.bookshelfLayoutGridLandscape, 7)
 
+    /**
+     * 网格模式布局样式: 0:标准, 1:紧凑, 2:仅封面
+     */
+    var bookshelfGridLayout by prefDelegate(PreferKey.bookshelfGridLayout, 0)
+
     /**
      * 紧凑模式
      */

+ 79 - 7
app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt

@@ -26,23 +26,30 @@ import androidx.compose.ui.draw.shadow
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
+import io.legado.app.R
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookGroup
+import io.legado.app.help.book.isLocal
+import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
 import io.legado.app.ui.widget.components.cover.BookCover
+import io.legado.app.ui.widget.components.cover.BookCoverWithProgress
+import io.legado.app.utils.toTimeAgo
 
 /**
  * 通用的书架条目布局组件
- * 支持 列表/网格 模式及 紧凑/常规 样式
+ * 支持 列表/网格 模式及 标准/紧凑/仅封面 样式
  */
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun BookshelfItem(
     isGrid: Boolean,
-    isCompact: Boolean,
+    gridStyle: Int, // 0: Standard, 1: Compact, 2: Cover Only
+    isCompact: Boolean, // For List Mode
     cover: @Composable (Modifier) -> Unit,
     title: String,
     modifier: Modifier = Modifier,
@@ -57,7 +64,6 @@ fun BookshelfItem(
     onLongClick: () -> Unit
 ) {
     if (isGrid) {
-
         Box(
             modifier = modifier
                 .clip(MaterialTheme.shapes.small)
@@ -80,7 +86,7 @@ fun BookshelfItem(
                     modifier = Modifier.clip(MaterialTheme.shapes.extraSmall)
                 ) {
                     cover(coverModifier)
-                    if (isCompact) {
+                    if (gridStyle == 1) {
                         Text(
                             text = title,
                             style = (if (titleSmallFont) MaterialTheme.typography.labelSmall else MaterialTheme.typography.labelMedium).copy(
@@ -108,7 +114,7 @@ fun BookshelfItem(
                     }
                 }
 
-                if (!isCompact) {
+                if (gridStyle == 0) {
                     Text(
                         text = title,
                         style = if (titleSmallFont) MaterialTheme.typography.labelSmall else MaterialTheme.typography.labelMedium,
@@ -272,7 +278,7 @@ fun BookGroupCover(
 fun BookGroupItemGrid(
     group: BookGroup,
     previewBooks: List<Book>,
-    isCompact: Boolean = false,
+    gridStyle: Int = 0,
     titleSmallFont: Boolean = false,
     titleCenter: Boolean = true,
     titleMaxLines: Int = 2,
@@ -283,7 +289,8 @@ fun BookGroupItemGrid(
 ) {
     BookshelfItem(
         isGrid = true,
-        isCompact = isCompact,
+        gridStyle = gridStyle,
+        isCompact = false,
         cover = { BookGroupCover(books = previewBooks, modifier = it) },
         title = group.groupName,
         modifier = modifier,
@@ -311,6 +318,7 @@ fun BookGroupItemList(
 ) {
     BookshelfItem(
         isGrid = false,
+        gridStyle = 0,
         isCompact = isCompact,
         cover = { BookGroupCover(books = previewBooks, modifier = it) },
         title = group.groupName,
@@ -325,3 +333,67 @@ fun BookGroupItemList(
         onLongClick = onLongClick
     )
 }
+
+@Composable
+fun BookItem(
+    book: Book,
+    layoutMode: Int,
+    gridStyle: Int = 0,
+    isCompact: Boolean = false,
+    isUpdating: Boolean = false,
+    titleSmallFont: Boolean = false,
+    titleCenter: Boolean = true,
+    titleMaxLines: Int = 2,
+    coverShadow: Boolean = false,
+    onClick: () -> Unit,
+    onLongClick: () -> Unit
+) {
+    val unreadCount = book.getUnreadChapterNum()
+
+    BookshelfItem(
+        isGrid = layoutMode != 0,
+        gridStyle = gridStyle,
+        isCompact = isCompact,
+        cover = { modifier ->
+            BookCoverWithProgress(
+                name = book.name,
+                author = book.author,
+                path = book.getDisplayCover(),
+                isUpdating = isUpdating,
+                modifier = modifier,
+                badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null,
+                showBadgeDot = !BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && unreadCount > 0 && book.lastCheckCount > 0
+            )
+        },
+        title = book.name,
+        subTitle = if (layoutMode == 0 && isCompact) {
+            stringResource(R.string.author_read, book.author, unreadCount)
+        } else {
+            book.author
+        },
+        desc = stringResource(R.string.read_dur_progress, book.durChapterTitle ?: ""),
+        extra = {
+            if (BookshelfConfig.showLastUpdateTime && !book.isLocal) {
+                Text(
+                    text = book.latestChapterTime.toTimeAgo(),
+                    style = MaterialTheme.typography.bodySmall,
+                    color = if (layoutMode != 0 || !isCompact) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary,
+                    modifier = Modifier.padding(end = 4.dp)
+                )
+            }
+            Text(
+                text = book.latestChapterTitle ?: "",
+                style = MaterialTheme.typography.bodySmall,
+                maxLines = 1,
+                overflow = TextOverflow.Ellipsis,
+                modifier = Modifier.weight(1f)
+            )
+        },
+        titleSmallFont = titleSmallFont,
+        titleCenter = titleCenter,
+        titleMaxLines = titleMaxLines,
+        coverShadow = coverShadow,
+        onClick = onClick,
+        onLongClick = onLongClick
+    )
+}

+ 29 - 18
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt

@@ -33,7 +33,7 @@ import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
 import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
 import io.legado.app.ui.widget.components.settingItem.CompactSliderSettingItem
-import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
+import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
@@ -107,13 +107,24 @@ fun BookshelfConfigSheet(
                 }
             )
 
-            SwitchSettingItem(
-                title = "紧凑模式",
-                checked = BookshelfConfig.bookshelfLayoutCompact,
-                imageVector = Icons.Default.ViewCompact,
-                color = MaterialTheme.colorScheme.surface,
-                onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
-            )
+            if (layoutMode == 1) {
+                CompactDropdownSettingItem(
+                    title = "网格样式",
+                    selectedValue = BookshelfConfig.bookshelfGridLayout.toString(),
+                    displayEntries = stringArrayResource(R.array.bookshelf_grid_layout),
+                    entryValues = Array(stringArrayResource(R.array.bookshelf_grid_layout).size) { it.toString() },
+                    imageVector = Icons.Default.ViewCompact,
+                    onValueChange = { BookshelfConfig.bookshelfGridLayout = it.toInt() }
+                )
+            } else {
+                CompactSwitchSettingItem(
+                    title = "紧凑模式",
+                    checked = BookshelfConfig.bookshelfLayoutCompact,
+                    imageVector = Icons.Default.ViewCompact,
+                    color = MaterialTheme.colorScheme.surface,
+                    onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
+                )
+            }
 
             // Grid Count
             val gridCount =
@@ -129,7 +140,7 @@ fun BookshelfConfigSheet(
                 }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = "标题小字体",
                 checked = BookshelfConfig.bookshelfTitleSmallFont,
                 imageVector = Icons.Default.TextFormat,
@@ -137,7 +148,7 @@ fun BookshelfConfigSheet(
                 onCheckedChange = { BookshelfConfig.bookshelfTitleSmallFont = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = "标题居中",
                 checked = BookshelfConfig.bookshelfTitleCenter,
                 color = MaterialTheme.colorScheme.surface,
@@ -152,7 +163,7 @@ fun BookshelfConfigSheet(
                 onValueChange = { BookshelfConfig.bookshelfTitleMaxLines = it.toInt() }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = "封面阴影",
                 checked = BookshelfConfig.bookshelfCoverShadow,
                 imageVector = Icons.Default.BlurOn,
@@ -161,7 +172,7 @@ fun BookshelfConfigSheet(
             )
 
             // Switches
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_unread),
                 checked = BookshelfConfig.showUnread,
                 imageVector = Icons.Default.Notifications,
@@ -169,14 +180,14 @@ fun BookshelfConfigSheet(
                 onCheckedChange = { BookshelfConfig.showUnread = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_unread_new),
                 checked = BookshelfConfig.showUnreadNew,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.showUnreadNew = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_tip),
                 checked = BookshelfConfig.showTip,
                 imageVector = Icons.Default.Info,
@@ -184,7 +195,7 @@ fun BookshelfConfigSheet(
                 onCheckedChange = { BookshelfConfig.showTip = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_last_update_time),
                 checked = BookshelfConfig.showLastUpdateTime,
                 imageVector = Icons.Default.History,
@@ -192,21 +203,21 @@ fun BookshelfConfigSheet(
                 onCheckedChange = { BookshelfConfig.showLastUpdateTime = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_wait_up_count),
                 checked = BookshelfConfig.showWaitUpCount,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.showWaitUpCount = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_bookshelf_fast_scroller),
                 checked = BookshelfConfig.showBookshelfFastScroller,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.showBookshelfFastScroller = it }
             )
 
-            SwitchSettingItem(
+            CompactSwitchSettingItem(
                 title = stringResource(R.string.show_bookshelf_tab_menu),
                 checked = BookshelfConfig.shouldShowExpandButton,
                 imageVector = Icons.Default.Menu,

+ 2 - 66
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt

@@ -57,7 +57,6 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
 import io.legado.app.R
 import io.legado.app.data.entities.Book
-import io.legado.app.help.book.isLocal
 import io.legado.app.ui.book.cache.CacheActivity
 import io.legado.app.ui.book.import.local.ImportBookActivity
 import io.legado.app.ui.book.import.remote.RemoteBookActivity
@@ -66,14 +65,12 @@ import io.legado.app.ui.book.search.SearchActivity
 import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
 import io.legado.app.ui.file.HandleFileContract
 import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
-import io.legado.app.ui.widget.components.cover.BookCoverWithProgress
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
 import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
 import io.legado.app.ui.widget.components.list.ListScaffold
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.utils.readText
 import io.legado.app.utils.startActivity
-import io.legado.app.utils.toTimeAgo
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
@@ -332,7 +329,7 @@ fun BookshelfScreen(
                             BookGroupItemGrid(
                                 group = group,
                                 previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(),
-                                isCompact = BookshelfConfig.bookshelfLayoutCompact,
+                                gridStyle = BookshelfConfig.bookshelfGridLayout,
                                 titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
                                 titleCenter = BookshelfConfig.bookshelfTitleCenter,
                                 titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
@@ -462,6 +459,7 @@ fun BookshelfPage(
             BookItem(
                 book = book,
                 layoutMode = bookshelfLayoutMode,
+                gridStyle = BookshelfConfig.bookshelfGridLayout,
                 isCompact = BookshelfConfig.bookshelfLayoutCompact,
                 isUpdating = uiState.updatingBooks.contains(book.bookUrl),
                 titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
@@ -474,65 +472,3 @@ fun BookshelfPage(
         }
     }
 }
-
-@Composable
-fun BookItem(
-    book: Book,
-    layoutMode: Int,
-    isCompact: Boolean = false,
-    isUpdating: Boolean = false,
-    titleSmallFont: Boolean = false,
-    titleCenter: Boolean = true,
-    titleMaxLines: Int = 2,
-    coverShadow: Boolean = false,
-    onClick: () -> Unit,
-    onLongClick: () -> Unit
-) {
-    val unreadCount = book.getUnreadChapterNum()
-
-    BookshelfItem(
-        isGrid = layoutMode != 0,
-        isCompact = isCompact,
-        cover = { modifier ->
-            BookCoverWithProgress(
-                name = book.name,
-                author = book.author,
-                path = book.getDisplayCover(),
-                isUpdating = isUpdating,
-                modifier = modifier,
-                badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null,
-                showBadgeDot = !BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && unreadCount > 0 && book.lastCheckCount > 0
-            )
-        },
-        title = book.name,
-        subTitle = if (isCompact && layoutMode == 0) {
-            stringResource(R.string.author_read, book.author, unreadCount)
-        } else {
-            book.author
-        },
-        desc = stringResource(R.string.read_dur_progress, book.durChapterTitle ?: ""),
-        extra = {
-            if (BookshelfConfig.showLastUpdateTime && !book.isLocal) {
-                Text(
-                    text = book.latestChapterTime.toTimeAgo(),
-                    style = MaterialTheme.typography.bodySmall,
-                    color = if (!isCompact) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary,
-                    modifier = Modifier.padding(end = 4.dp)
-                )
-            }
-            Text(
-                text = book.latestChapterTitle ?: "",
-                style = MaterialTheme.typography.bodySmall,
-                maxLines = 1,
-                overflow = TextOverflow.Ellipsis,
-                modifier = Modifier.weight(1f)
-            )
-        },
-        titleSmallFont = titleSmallFont,
-        titleCenter = titleCenter,
-        titleMaxLines = titleMaxLines,
-        coverShadow = coverShadow,
-        onClick = onClick,
-        onLongClick = onLongClick
-    )
-}

+ 64 - 51
app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt

@@ -1,5 +1,10 @@
 package io.legado.app.ui.main.bookshelf
 
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.SizeTransform
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
@@ -73,63 +78,71 @@ fun GroupManageSheet(
     }
 
     GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
-        if (isEditing) {
-            GroupEditContent(
-                group = editingGroup,
-                onDismissRequest = { isEditing = false },
-                viewModel = viewModel
-            )
-        } else {
-            Column(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .padding(bottom = 32.dp)
-            ) {
-                Row(
+        AnimatedContent(
+            targetState = isEditing,
+            transitionSpec = {
+                fadeIn() togetherWith fadeOut() using SizeTransform(clip = false)
+            },
+            label = "GroupManageState"
+        ) { editing ->
+            if (editing) {
+                GroupEditContent(
+                    group = editingGroup,
+                    onDismissRequest = { isEditing = false },
+                    viewModel = viewModel
+                )
+            } else {
+                Column(
                     modifier = Modifier
                         .fillMaxWidth()
-                        .padding(horizontal = 16.dp)
-                        .padding(vertical = 8.dp),
-                    horizontalArrangement = Arrangement.SpaceBetween,
-                    verticalAlignment = Alignment.CenterVertically
+                        .padding(bottom = 32.dp)
                 ) {
-                    Text(
-                        text = stringResource(R.string.group_manage),
-                        style = MaterialTheme.typography.titleLarge
-                    )
-                    IconButton(onClick = {
-                        editingGroup = null
-                        isEditing = true
-                    }) {
-                        Icon(
-                            Icons.Default.Add,
-                            contentDescription = stringResource(R.string.add)
+                    Row(
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(horizontal = 16.dp)
+                            .padding(vertical = 8.dp),
+                        horizontalArrangement = Arrangement.SpaceBetween,
+                        verticalAlignment = Alignment.CenterVertically
+                    ) {
+                        Text(
+                            text = stringResource(R.string.group_manage),
+                            style = MaterialTheme.typography.titleLarge
                         )
+                        IconButton(onClick = {
+                            editingGroup = null
+                            isEditing = true
+                        }) {
+                            Icon(
+                                Icons.Default.Add,
+                                contentDescription = stringResource(R.string.add)
+                            )
+                        }
                     }
-                }
 
-                LazyColumn(
-                    state = listState,
-                    modifier = Modifier.fillMaxWidth(),
-                    verticalArrangement = Arrangement.spacedBy(8.dp)
-                ) {
-                    items(listData, key = { it.groupId }) { group ->
-                        val manageNameInfo = remember(group) { group.getManageName(context) }
-                        ReorderableSelectionItem(
-                            state = reorderableState,
-                            key = group.groupId,
-                            title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
-                            subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
-                            isEnabled = group.show,
-                            containerColor = MaterialTheme.colorScheme.surface,
-                            onEnabledChange = { isChecked ->
-                                viewModel.upGroup(group.copy(show = isChecked))
-                            },
-                            onClickEdit = {
-                                editingGroup = group
-                                isEditing = true
-                            }
-                        )
+                    LazyColumn(
+                        state = listState,
+                        modifier = Modifier.fillMaxWidth(),
+                        verticalArrangement = Arrangement.spacedBy(8.dp)
+                    ) {
+                        items(listData, key = { it.groupId }) { group ->
+                            val manageNameInfo = remember(group) { group.getManageName(context) }
+                            ReorderableSelectionItem(
+                                state = reorderableState,
+                                key = group.groupId,
+                                title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
+                                subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
+                                isEnabled = group.show,
+                                containerColor = MaterialTheme.colorScheme.surface,
+                                onEnabledChange = { isChecked ->
+                                    viewModel.upGroup(group.copy(show = isChecked))
+                                },
+                                onClickEdit = {
+                                    editingGroup = group
+                                    isEditing = true
+                                }
+                            )
+                        }
                     }
                 }
             }

+ 11 - 10
app/src/main/java/io/legado/app/ui/widget/components/cover/BookCover.kt

@@ -8,7 +8,6 @@ import android.text.TextPaint
 import android.text.TextUtils
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.background
-import androidx.compose.foundation.border
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.aspectRatio
@@ -79,15 +78,17 @@ fun BookCover(
             )
             .clip(RoundedCornerShape(4.dp))
             .background(MaterialTheme.colorScheme.surfaceContainerLow)
-            .then(
-                if (CoverConfig.coverShowStroke) {
-                    Modifier.border(
-                        0.5.dp,
-                        MaterialTheme.colorScheme.outlineVariant,
-                        RoundedCornerShape(4.dp)
-                    )
-                } else Modifier
-            )
+        /*
+        .then(
+            if (CoverConfig.coverShowStroke) {
+                Modifier.border(
+                    0.5.dp,
+                    MaterialTheme.colorScheme.outlineVariant,
+                    RoundedCornerShape(4.dp)
+                )
+            } else Modifier
+        )
+         */
     ) {
         // 1. 封面底图
         AsyncImage(

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

@@ -1,6 +1,5 @@
 package io.legado.app.ui.widget.components.modalBottomSheet
 
-import androidx.compose.animation.animateContentSize
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -47,7 +46,6 @@ fun GlassModalBottomSheet(
             Column(
                 modifier = Modifier
                     .fillMaxWidth()
-                    .animateContentSize()
                     .then(modifier),
                 content = content
             )

+ 31 - 0
app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt

@@ -12,6 +12,7 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Slider
+import androidx.compose.material3.Switch
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -21,6 +22,7 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.scale
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.vector.ImageVector
@@ -151,3 +153,32 @@ fun CompactSliderSettingItem(
         }
     )
 }
+
+@Composable
+fun CompactSwitchSettingItem(
+    title: String,
+    checked: Boolean,
+    description: String? = null,
+    imageVector: ImageVector? = null,
+    color: Color? = MaterialTheme.colorScheme.surface,
+    shape: Shape = MaterialTheme.shapes.small,
+    enabled: Boolean = true,
+    onCheckedChange: (Boolean) -> Unit
+) {
+    SettingItem(
+        title = title,
+        description = description,
+        imageVector = imageVector,
+        color = color,
+        shape = shape,
+        onClick = { if (enabled) onCheckedChange(!checked) },
+        trailingContent = {
+            Switch(
+                modifier = Modifier.scale(0.8f),
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                enabled = enabled
+            )
+        }
+    )
+}

+ 6 - 0
app/src/main/res/values-zh-rCN/arrays.xml

@@ -208,5 +208,11 @@
         <item>不显示</item>
     </string-array>
 
+    <string-array name="bookshelf_grid_layout">
+        <item>标准</item>
+        <item>紧凑</item>
+        <item>仅封面</item>
+    </string-array>
+
     <string name="all_version">所有版本</string>
 </resources>

+ 6 - 0
app/src/main/res/values-zh-rHK/arrays.xml

@@ -87,4 +87,10 @@
         <item>替換規則</item>
     </string-array>
 
+    <string-array name="bookshelf_grid_layout">
+        <item>标准</item>
+        <item>紧凑</item>
+        <item>仅封面</item>
+    </string-array>
+
 </resources>

+ 6 - 0
app/src/main/res/values-zh-rTW/arrays.xml

@@ -124,4 +124,10 @@
         <item>取代規則</item>
     </string-array>
 
+    <string-array name="bookshelf_grid_layout">
+        <item>标准</item>
+        <item>紧凑</item>
+        <item>仅封面</item>
+    </string-array>
+
 </resources>

+ 7 - 1
app/src/main/res/values/arrays.xml

@@ -286,7 +286,13 @@
 		<item>@string/cover_grid</item>
 	</string-array>
 
-    <string-array name="label_vis_mode" >
+	<string-array name="bookshelf_grid_layout">
+		<item>Standard</item>
+		<item>Compact</item>
+		<item>Cover Only</item>
+	</string-array>
+
+	<string-array name="label_vis_mode" >
 		<item>Auto</item>
 		<item>Selected</item>
 		<item>Labeled</item>