新增文件夹列表横排封面布局

This commit is contained in:
HapeLee
2026-05-12 19:47:22 +08:00
parent b3a294ff1e
commit 56b28009e3
4 changed files with 257 additions and 69 deletions
@@ -270,6 +270,8 @@ object PreferKey {
const val bookshelfCoverShadow = "bookshelfCoverShadow" const val bookshelfCoverShadow = "bookshelfCoverShadow"
const val bookshelfSearchActionDirectToSearch = "bookshelfSearchActionDirectToSearch" const val bookshelfSearchActionDirectToSearch = "bookshelfSearchActionDirectToSearch"
const val bookshelfCardColor = "bookshelfCardColor" const val bookshelfCardColor = "bookshelfCardColor"
const val bookshelfGroupListStyle = "bookshelfGroupListStyle"
const val bookshelfGroupCoverCount = "bookshelfGroupCoverCount"
const val exploreLayoutGridLandscape = "exploreLayoutGridLandscape" const val exploreLayoutGridLandscape = "exploreLayoutGridLandscape"
@@ -58,6 +58,16 @@ object BookshelfConfig {
*/ */
var showBookIntro by prefDelegate(PreferKey.showBookIntro, false) var showBookIntro by prefDelegate(PreferKey.showBookIntro, false)
/**
* 是否显示简介
*/
var bookshelfShowIntro by prefDelegate(PreferKey.bookshelfShowIntro, true)
/**
* 是否显示标签
*/
var bookshelfShowTag by prefDelegate(PreferKey.bookshelfShowTag, true)
/** /**
* 列表模式下简介显示行数 (0为显示全部) * 列表模式下简介显示行数 (0为显示全部)
*/ */
@@ -155,6 +165,16 @@ object BookshelfConfig {
postEvent(EventBus.NOTIFY_MAIN, false) postEvent(EventBus.NOTIFY_MAIN, false)
} }
/**
* 文件夹在列表模式下的样式: 0: 默认, 1: 横排封面
*/
var bookshelfGroupListStyle by prefDelegate(PreferKey.bookshelfGroupListStyle, 0)
/**
* 文件夹在列表模式下横排封面的数量
*/
var bookshelfGroupCoverCount by prefDelegate(PreferKey.bookshelfGroupCoverCount, 4)
/** /**
* 书架搜索按钮是否直接跳转搜索页 * 书架搜索按钮是否直接跳转搜索页
*/ */
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -20,6 +21,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -55,6 +58,8 @@ import io.legado.app.ui.widget.components.card.NormalCard
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.cover.CoilBookCover import io.legado.app.ui.widget.components.cover.CoilBookCover
import io.legado.app.ui.widget.components.cover.BookshelfCover import io.legado.app.ui.widget.components.cover.BookshelfCover
import io.legado.app.ui.widget.components.icon.AppIcon
import io.legado.app.ui.widget.components.icon.AppIcons
import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.splitNotBlank import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.toTimeAgo import io.legado.app.utils.toTimeAgo
@@ -134,6 +139,7 @@ fun BookshelfItem(
if (isGrid) { if (isGrid) {
Box( Box(
modifier = modifier modifier = modifier
.clip(RoundedCornerShape(4.dp))
.combinedClickable( .combinedClickable(
onClick = onClick, onClick = onClick,
onLongClick = onLongClick onLongClick = onLongClick
@@ -450,16 +456,27 @@ fun BookGroupItemGrid(
fun BookGroupItemList( fun BookGroupItemList(
group: BookGroupUi, group: BookGroupUi,
previewBooks: List<BookShelfItem>, previewBooks: List<BookShelfItem>,
onClick: () -> Unit,
modifier: Modifier = Modifier,
countText: String? = null, countText: String? = null,
isCompact: Boolean = false, isCompact: Boolean = false,
titleSmallFont: Boolean = false, titleSmallFont: Boolean = false,
titleCenter: Boolean = true, titleCenter: Boolean = true,
titleMaxLines: Int = 2, titleMaxLines: Int = 2,
coverShadow: Boolean = false, coverShadow: Boolean = false,
modifier: Modifier = Modifier, onLongClick: (() -> Unit)? = null
onClick: () -> Unit,
onLongClick: (() -> Unit)?
) { ) {
if (BookshelfConfig.bookshelfGroupListStyle == 2) {
BookGroupItemHorizontalCovers(
group = group,
previewBooks = previewBooks,
onClick = onClick,
modifier = modifier,
countText = countText,
onLongClick = onLongClick
)
return
}
val firstBookName = previewBooks.firstOrNull()?.name val firstBookName = previewBooks.firstOrNull()?.name
val primaryColor = MaterialTheme.colorScheme.primary val primaryColor = MaterialTheme.colorScheme.primary
val descAnnotated = if (firstBookName != null) { val descAnnotated = if (firstBookName != null) {
@@ -475,7 +492,7 @@ fun BookGroupItemList(
BookshelfItem( BookshelfItem(
isGrid = false, isGrid = false,
gridStyle = 0, gridStyle = 0,
isCompact = isCompact, isCompact = BookshelfConfig.bookshelfGroupListStyle == 1 || isCompact,
cover = { BookGroupCover(books = previewBooks, coverPath = group.cover, modifier = it) }, cover = { BookGroupCover(books = previewBooks, coverPath = group.cover, modifier = it) },
title = group.groupName, title = group.groupName,
titleColor = primaryColor, titleColor = primaryColor,
@@ -491,6 +508,99 @@ fun BookGroupItemList(
) )
} }
@Composable
fun BookGroupItemHorizontalCovers(
group: BookGroupUi,
previewBooks: List<BookShelfItem>,
onClick: () -> Unit,
modifier: Modifier = Modifier,
countText: String? = null,
onLongClick: (() -> Unit)? = null
) {
Column {
NormalCard(
modifier = modifier
.fillMaxWidth()
.padding(all = 4.dp),
cornerRadius = 12.dp,
containerColor = if (BookshelfConfig.bookshelfCardColor != 0) {
Color(BookshelfConfig.bookshelfCardColor)
} else {
LegadoTheme.colorScheme.cardContainer
},
onClick = onClick,
onLongClick = onLongClick
) {
Column(
modifier = Modifier
.padding(vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, top = 4.dp, bottom = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
AppText(
text = group.groupName,
style = LegadoTheme.typography.titleMediumEmphasized,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
if (countText != null) {
AppText(
text = countText,
style = LegadoTheme.typography.labelSmallEmphasized,
color = LegadoTheme.colorScheme.onSurfaceVariant
)
}
AppIcon(
modifier = Modifier.padding(end = 6.dp),
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = "",
tint = LegadoTheme.colorScheme.onSurfaceVariant
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
val coverCount = BookshelfConfig.bookshelfGroupCoverCount
previewBooks.take(coverCount).forEach { book ->
Box(
modifier = Modifier
.weight(1f)
.aspectRatio(5f / 7f)
.clip(RoundedCornerShape(4.dp))
) {
CoilBookCover(
name = book.name,
author = book.author,
path = book.getDisplayCover(),
modifier = Modifier.fillMaxSize()
)
}
}
repeat(maxOf(0, coverCount - previewBooks.size)) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
}
if (BookshelfConfig.bookshelfShowDivider)
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
thickness = 0.5.dp,
color = LegadoTheme.colorScheme.outlineVariant.copy(alpha = 0.5f)
)
}
}
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun BookItem( fun BookItem(
@@ -582,7 +692,7 @@ fun BookItem(
val kindList = book.kind?.splitNotBlank(",", "\n")?.filter { it.isNotBlank() } val kindList = book.kind?.splitNotBlank(",", "\n")?.filter { it.isNotBlank() }
val intro = book.intro?.takeIf { it.isNotBlank() } val intro = book.intro?.takeIf { it.isNotBlank() }
val customTagColors = if (ThemeConfig.enableCustomTagColors) ThemeConfig.getCustomTagColors() else emptyList() val customTagColors = if (ThemeConfig.enableCustomTagColors) ThemeConfig.getCustomTagColors() else emptyList()
if (!kindList.isNullOrEmpty()) { if (BookshelfConfig.bookshelfShowTag && !kindList.isNullOrEmpty()) {
FlowRow( FlowRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -608,7 +718,7 @@ fun BookItem(
} }
} }
} }
if (intro != null) { if (BookshelfConfig.bookshelfShowIntro && intro != null) {
val maxLines = if (BookshelfConfig.bookshelfIntroMaxLines == 0) Int.MAX_VALUE else BookshelfConfig.bookshelfIntroMaxLines val maxLines = if (BookshelfConfig.bookshelfIntroMaxLines == 0) Int.MAX_VALUE else BookshelfConfig.bookshelfIntroMaxLines
AppText( AppText(
text = intro, text = intro,
@@ -108,6 +108,31 @@ fun BookshelfConfigSheet(
} }
) )
AnimatedVisibility(
visible = BookshelfConfig.bookGroupStyle == 2 && layoutMode == 0
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
CompactDropdownSettingItem(
title = "文件夹列表样式",
selectedValue = BookshelfConfig.bookshelfGroupListStyle.toString(),
displayEntries = arrayOf("默认", "紧凑", "横排封面"),
entryValues = arrayOf("0", "1", "2"),
onValueChange = { BookshelfConfig.bookshelfGroupListStyle = it.toInt() }
)
AnimatedVisibility(visible = BookshelfConfig.bookshelfGroupListStyle == 2) {
CompactSliderSettingItem(
title = "横排封面数量",
value = BookshelfConfig.bookshelfGroupCoverCount.toFloat(),
valueRange = 1f..10f,
steps = 9,
onValueChange = { BookshelfConfig.bookshelfGroupCoverCount = it.toInt() }
)
}
}
}
AnimatedVisibility( AnimatedVisibility(
visible = layoutMode == 1 visible = layoutMode == 1
) { ) {
@@ -155,78 +180,109 @@ fun BookshelfConfigSheet(
AnimatedVisibility( AnimatedVisibility(
visible = layoutMode != 1 visible = layoutMode != 1
) { ) {
Column( val isHorizontalCovers = BookshelfConfig.bookGroupStyle == 2 && BookshelfConfig.bookshelfGroupListStyle == 2
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
CompactSwitchSettingItem(
title = stringResource(R.string.compact_mode),
checked = BookshelfConfig.bookshelfLayoutCompact,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
)
CompactSwitchSettingItem( AnimatedVisibility(visible = !isHorizontalCovers) {
title = stringResource(R.string.show_divider_line),
checked = BookshelfConfig.bookshelfShowDivider,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfShowDivider = it }
)
val listColCount =
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
CompactSliderSettingItem(
title = stringResource(R.string.number_rows_columns),
value = listColCount.toFloat(),
valueRange = 1f..5f,
steps = 4,
onValueChange = {
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape =
it.toInt()
else BookshelfConfig.bookshelfLayoutListPortrait = it.toInt()
}
)
CompactClickableSettingItem(
title = "卡片背景颜色",
color = LegadoTheme.colorScheme.surface,
onClick = { showColorPicker = true }
)
CompactSwitchSettingItem(
title = "显示更多信息",
checked = BookshelfConfig.showBookIntro,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.showBookIntro = it }
)
AnimatedVisibility(visible = BookshelfConfig.showBookIntro) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
CompactSliderSettingItem( if (BookshelfConfig.bookGroupStyle != 2) {
title = "简介行数", CompactSwitchSettingItem(
description = if (BookshelfConfig.bookshelfIntroMaxLines == 0) "显示全部简介" else "显示 ${BookshelfConfig.bookshelfIntroMaxLines} 行简介", title = stringResource(R.string.compact_mode),
value = BookshelfConfig.bookshelfIntroMaxLines.toFloat(), checked = BookshelfConfig.bookshelfLayoutCompact,
valueRange = 0f..10f,
steps = 10,
onValueChange = { BookshelfConfig.bookshelfIntroMaxLines = it.toInt() }
)
CompactSwitchSettingItem(
title = "自定义标签颜色",
checked = ThemeConfig.enableCustomTagColors,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { ThemeConfig.enableCustomTagColors = it }
)
AnimatedVisibility(visible = ThemeConfig.enableCustomTagColors) {
CompactClickableSettingItem(
title = "管理标签颜色",
color = LegadoTheme.colorScheme.surface, color = LegadoTheme.colorScheme.surface,
onClick = { showLabelColorManage = true } onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
) )
} }
CompactSwitchSettingItem(
title = stringResource(R.string.show_divider_line),
checked = BookshelfConfig.bookshelfShowDivider,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfShowDivider = it }
)
val listColCount =
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
CompactSliderSettingItem(
title = stringResource(R.string.number_rows_columns),
value = listColCount.toFloat(),
valueRange = 1f..5f,
steps = 4,
onValueChange = {
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape =
it.toInt()
else BookshelfConfig.bookshelfLayoutListPortrait = it.toInt()
}
)
CompactClickableSettingItem(
title = "卡片背景颜色",
color = LegadoTheme.colorScheme.surface,
onClick = { showColorPicker = true }
)
CompactSwitchSettingItem(
title = "显示更多信息",
checked = BookshelfConfig.showBookIntro,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.showBookIntro = it }
)
AnimatedVisibility(visible = BookshelfConfig.showBookIntro) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
CompactSwitchSettingItem(
title = "显示简介",
checked = BookshelfConfig.bookshelfShowIntro,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfShowIntro = it }
)
AnimatedVisibility(
visible = BookshelfConfig.bookshelfShowIntro
) {
CompactSliderSettingItem(
title = "简介行数",
description = if (BookshelfConfig.bookshelfIntroMaxLines == 0) "显示全部简介" else "显示 ${BookshelfConfig.bookshelfIntroMaxLines} 行简介",
value = BookshelfConfig.bookshelfIntroMaxLines.toFloat(),
valueRange = 0f..10f,
steps = 10,
onValueChange = { BookshelfConfig.bookshelfIntroMaxLines = it.toInt() }
)
}
CompactSwitchSettingItem(
title = "显示标签",
checked = BookshelfConfig.bookshelfShowTag,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfShowTag = it }
)
AnimatedVisibility(
visible = BookshelfConfig.bookshelfShowTag
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
CompactSwitchSettingItem(
title = "自定义标签颜色",
checked = ThemeConfig.enableCustomTagColors,
color = LegadoTheme.colorScheme.surface,
onCheckedChange = { ThemeConfig.enableCustomTagColors = it }
)
AnimatedVisibility(visible = ThemeConfig.enableCustomTagColors) {
CompactClickableSettingItem(
title = "管理标签颜色",
color = LegadoTheme.colorScheme.surface,
onClick = { showLabelColorManage = true }
)
}
}
}
}
}
} }
} }
}
} }
CompactSliderSettingItem( CompactSliderSettingItem(