[优化] 实现了更多原书架功能(日志、快速滚动条等),修复了一些问题

This commit is contained in:
HapeLee committed 2026-03-21 04:18:55 +08:00
1 parent 26246bb441
commit 29ec76481a
12 files changed
+299 -57

No files matched your search

@@ -0,0 +1,124 @@
package io.legado.app.ui.about
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DeleteSweep
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
import io.legado.app.utils.LogUtils
import java.util.Date
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppLogSheet(
onDismissRequest: () -> Unit
) {
var logs by remember { mutableStateOf(AppLog.logs) }
var showStackTrace by remember { mutableStateOf<String?>(null) }
GlassModalBottomSheet(
onDismissRequest = onDismissRequest
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.log),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.weight(1f)
)
IconButton(onClick = {
AppLog.clear()
logs = emptyList()
}) {
Icon(Icons.Default.DeleteSweep, contentDescription = "Clear")
}
}
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(bottom = 16.dp)
) {
items(logs) { item ->
LogItem(item) {
item.third?.let {
showStackTrace = it.stackTraceToString()
}
}
}
}
}
showStackTrace?.let { stackTrace ->
AlertDialog(
onDismissRequest = { showStackTrace = null },
title = { Text("Log") },
text = {
SelectionContainer {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
Text(text = stackTrace, style = MaterialTheme.typography.bodySmall)
}
}
},
confirmButton = {
TextButton(onClick = { showStackTrace = null }) {
Text(stringResource(android.R.string.ok))
}
}
)
}
}
@Composable
private fun LogItem(
item: Triple<Long, String, Throwable?>,
onClick: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(16.dp)
) {
Text(
text = LogUtils.logTimeFormat.format(Date(item.first)),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.outline
)
Text(
text = item.second,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(top = 4.dp)
)
}
}
@@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -22,7 +22,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedIconButton import androidx.compose.material3.OutlinedIconButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -41,8 +40,8 @@ import io.legado.app.data.entities.BookGroup
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.components.cover.Cover import io.legado.app.ui.widget.components.cover.Cover
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
import io.legado.app.ui.widget.components.settingItem.SettingItem import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.MD5Utils import io.legado.app.utils.MD5Utils
import io.legado.app.utils.SelectImageContract import io.legado.app.utils.SelectImageContract
@@ -128,7 +127,7 @@ fun GroupEditContent(
Cover( Cover(
path = coverPath, path = coverPath,
modifier = Modifier modifier = Modifier
.size(width = 90.dp, height = 130.dp) .width(96.dp)
.clickable { selectImage.launch() } .clickable { selectImage.launch() }
) )
@@ -144,7 +143,7 @@ fun GroupEditContent(
singleLine = true singleLine = true
) )
DropdownListSettingItem( CompactDropdownSettingItem(
title = stringResource(R.string.sort), title = stringResource(R.string.sort),
selectedValue = selectedSortIndex.toString(), selectedValue = selectedSortIndex.toString(),
displayEntries = sortOptions, displayEntries = sortOptions,
@@ -158,15 +157,10 @@ fun GroupEditContent(
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
SettingItem( CompactSwitchSettingItem(
title = stringResource(R.string.allow_drop_down_refresh), title = stringResource(R.string.allow_drop_down_refresh),
trailingContent = { checked = enableRefresh,
Switch( onCheckedChange = { enableRefresh = it }
checked = enableRefresh,
onCheckedChange = { enableRefresh = it }
)
},
onClick = { enableRefresh = !enableRefresh }
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
@@ -74,18 +74,18 @@ fun BookshelfItem(
.padding(4.dp) .padding(4.dp)
) { ) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
val coverModifier = if (coverShadow) {
Modifier
.fillMaxWidth()
.shadow(4.dp, MaterialTheme.shapes.extraSmall)
} else {
Modifier.fillMaxWidth()
}
Box( Box(
modifier = Modifier.clip(MaterialTheme.shapes.extraSmall) modifier = Modifier
.then(
if (coverShadow) Modifier.shadow(
4.dp,
MaterialTheme.shapes.extraSmall
) else Modifier
)
.clip(MaterialTheme.shapes.extraSmall) // 先阴影后裁剪
) { ) {
cover(coverModifier) cover(Modifier.fillMaxWidth())
if (gridStyle == 1) { if (gridStyle == 1) {
Text( Text(
text = title, text = title,
@@ -96,6 +96,7 @@ fun BookshelfItem(
blurRadius = 4f blurRadius = 4f
) )
), ),
textAlign = if (titleCenter) TextAlign.Center else TextAlign.Start,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier modifier = Modifier
@@ -5,12 +5,12 @@ import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
@@ -35,8 +35,8 @@ import androidx.compose.material3.PrimaryScrollableTabRow
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.pullToRefresh
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -57,6 +57,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.ui.about.AppLogSheet
import io.legado.app.ui.book.cache.CacheActivity import io.legado.app.ui.book.cache.CacheActivity
import io.legado.app.ui.book.import.local.ImportBookActivity import io.legado.app.ui.book.import.local.ImportBookActivity
import io.legado.app.ui.book.import.remote.RemoteBookActivity import io.legado.app.ui.book.import.remote.RemoteBookActivity
@@ -67,6 +68,7 @@ import io.legado.app.ui.file.HandleFileContract
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet 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.importComponents.SourceInputDialog
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
import io.legado.app.ui.widget.components.list.ListScaffold import io.legado.app.ui.widget.components.list.ListScaffold
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.utils.readText import io.legado.app.utils.readText
@@ -94,6 +96,7 @@ fun BookshelfScreen(
var showImportSheet by remember { mutableStateOf(false) } var showImportSheet by remember { mutableStateOf(false) }
var showConfigSheet by remember { mutableStateOf(false) } var showConfigSheet by remember { mutableStateOf(false) }
var showGroupManageSheet by remember { mutableStateOf(false) } var showGroupManageSheet by remember { mutableStateOf(false) }
var showLogSheet by remember { mutableStateOf(false) }
val importLauncher = rememberLauncherForActivityResult(HandleFileContract()) { val importLauncher = rememberLauncherForActivityResult(HandleFileContract()) {
runCatching { runCatching {
@@ -232,6 +235,7 @@ fun BookshelfScreen(
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text(stringResource(R.string.log)) }, text = { Text(stringResource(R.string.log)) },
onClick = { onClick = {
showLogSheet = true
dismiss() dismiss()
}, },
leadingIcon = { Icon(Icons.Default.History, null) } leadingIcon = { Icon(Icons.Default.History, null) }
@@ -240,8 +244,11 @@ fun BookshelfScreen(
bottomContent = if (bookGroupStyle == 0) { bottomContent = if (bookGroupStyle == 0) {
{ {
if (uiState.groups.isNotEmpty()) { if (uiState.groups.isNotEmpty()) {
val selectedTabIndex = remember(pagerState.currentPage, uiState.groups.size) {
pagerState.currentPage.coerceIn(0, uiState.groups.size - 1)
}
PrimaryScrollableTabRow( PrimaryScrollableTabRow(
selectedTabIndex = pagerState.currentPage, selectedTabIndex = selectedTabIndex,
edgePadding = 0.dp, edgePadding = 0.dp,
divider = { }, divider = { },
containerColor = GlassTopAppBarDefaults.containerColor(), containerColor = GlassTopAppBarDefaults.containerColor(),
@@ -249,7 +256,7 @@ fun BookshelfScreen(
) { ) {
uiState.groups.forEachIndexed { index, group -> uiState.groups.forEachIndexed { index, group ->
Tab( Tab(
selected = pagerState.currentPage == index, selected = selectedTabIndex == index,
onClick = { onClick = {
scope.launch { pagerState.animateScrollToPage(index) } scope.launch { pagerState.animateScrollToPage(index) }
}, },
@@ -271,31 +278,28 @@ fun BookshelfScreen(
) { paddingValues -> ) { paddingValues ->
var isRefreshing by remember { mutableStateOf(false) } var isRefreshing by remember { mutableStateOf(false) }
val pullToRefreshState = rememberPullToRefreshState() val pullToRefreshState = rememberPullToRefreshState()
val currentGroup = uiState.groups.getOrNull(pagerState.currentPage)
val pullToRefreshEnabled = currentGroup?.enableRefresh ?: true
PullToRefreshBox( Box(
isRefreshing = isRefreshing, modifier = Modifier
onRefresh = { .fillMaxSize()
scope.launch { .pullToRefresh(
isRefreshing = true
viewModel.upToc(uiState.items)
delay(1000)
isRefreshing = false
}
},
state = pullToRefreshState,
modifier = Modifier.fillMaxSize(),
indicator = {
PullToRefreshDefaults.LoadingIndicator(
state = pullToRefreshState, state = pullToRefreshState,
isRefreshing = isRefreshing, isRefreshing = isRefreshing,
modifier = Modifier onRefresh = {
.align(Alignment.TopCenter) scope.launch {
.padding(top = paddingValues.calculateTopPadding()) isRefreshing = true
viewModel.upToc(uiState.items)
delay(1000)
isRefreshing = false
}
},
enabled = pullToRefreshEnabled
) )
}
) { ) {
if (bookGroupStyle == 2 && isInFolderRoot) { if (bookGroupStyle == 2 && isInFolderRoot) {
LazyVerticalGrid( FastScrollLazyVerticalGrid(
columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)), columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues( contentPadding = PaddingValues(
@@ -305,7 +309,8 @@ fun BookshelfScreen(
end = if (bookshelfLayoutMode != 0) 12.dp else 0.dp end = if (bookshelfLayoutMode != 0) 12.dp else 0.dp
), ),
verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp), verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp) horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
showFastScroll = BookshelfConfig.showBookshelfFastScroller
) { ) {
items(uiState.groups) { group -> items(uiState.groups) { group ->
if (bookshelfLayoutMode == 0) { if (bookshelfLayoutMode == 0) {
@@ -369,6 +374,14 @@ fun BookshelfScreen(
} }
} }
} }
PullToRefreshDefaults.LoadingIndicator(
state = pullToRefreshState,
isRefreshing = isRefreshing,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = paddingValues.calculateTopPadding())
)
} }
} }
@@ -409,6 +422,10 @@ fun BookshelfScreen(
) )
} }
if (showLogSheet) {
AppLogSheet(onDismissRequest = { showLogSheet = false })
}
if (uiState.isLoading) { if (uiState.isLoading) {
Dialog(onDismissRequest = {}) { Dialog(onDismissRequest = {}) {
Surface( Surface(
@@ -443,7 +460,7 @@ fun BookshelfPage(
onBookClick: (Book) -> Unit, onBookClick: (Book) -> Unit,
onBookLongClick: (Book) -> Unit onBookLongClick: (Book) -> Unit
) { ) {
LazyVerticalGrid( FastScrollLazyVerticalGrid(
columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)), columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues( contentPadding = PaddingValues(
@@ -453,7 +470,8 @@ fun BookshelfPage(
end = if (bookshelfLayoutMode != 0) 12.dp else 0.dp end = if (bookshelfLayoutMode != 0) 12.dp else 0.dp
), ),
verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp), verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp) horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
showFastScroll = BookshelfConfig.showBookshelfFastScroller
) { ) {
items(books, key = { it.bookUrl }) { book -> items(books, key = { it.bookUrl }) { book ->
BookItem( BookItem(
@@ -95,7 +95,6 @@ fun GroupManageSheet(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = 32.dp)
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -0,0 +1,43 @@
package io.legado.app.ui.widget.components
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchColors
import androidx.compose.material3.SwitchDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
@Composable
fun IconSwitch(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
enabled: Boolean = true,
checkedIcon: ImageVector = Icons.Filled.Check,
uncheckedIcon: ImageVector? = null,
showIcon: Boolean = true,
colors: SwitchColors = SwitchDefaults.colors()
) {
Switch(
checked = checked,
onCheckedChange = onCheckedChange,
enabled = enabled,
colors = colors,
thumbContent = {
if (!showIcon) return@Switch
val icon = if (checked) checkedIcon else uncheckedIcon
if (icon != null) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize)
)
}
}
)
}
@@ -37,7 +37,6 @@ import androidx.compose.ui.unit.dp
import androidx.core.graphics.withSave import androidx.core.graphics.withSave
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import io.legado.app.help.config.AppConfig
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.ui.config.coverConfig.CoverConfig import io.legado.app.ui.config.coverConfig.CoverConfig
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
@@ -57,7 +56,7 @@ fun BookCover(
val context = LocalContext.current val context = LocalContext.current
val isNight = isSystemInDarkTheme() val isNight = isSystemInDarkTheme()
val useDefault = AppConfig.useDefaultCover val useDefault = CoverConfig.useDefaultCover
val finalPath = if (useDefault) null else path val finalPath = if (useDefault) null else path
// 为每个 BookCover 组件实例基于其书名或路径生成一个随机封面 // 为每个 BookCover 组件实例基于其书名或路径生成一个随机封面
@@ -43,7 +43,7 @@ fun Cover(
val boxModifier = modifier val boxModifier = modifier
.aspectRatio(3f / 4f) .aspectRatio(3f / 4f)
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colorScheme.surfaceContainerLow) .background(MaterialTheme.colorScheme.surfaceContainer)
Box(modifier = boxModifier) { Box(modifier = boxModifier) {
if (path == null) { if (path == null) {
@@ -6,6 +6,11 @@ import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -80,3 +85,52 @@ fun FastScrollLazyColumn(
) )
} }
} }
@Composable
fun FastScrollLazyVerticalGrid(
columns: GridCells,
modifier: Modifier = Modifier,
state: LazyGridState = rememberLazyGridState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
userScrollEnabled: Boolean = true,
showFastScroll: Boolean = true,
content: LazyGridScope.() -> Unit,
) {
if (showFastScroll) {
VerticalGridFastScroller(
state = state,
columns = columns,
arrangement = horizontalArrangement,
contentPadding = contentPadding,
modifier = modifier,
topContentPadding = contentPadding.calculateTopPadding(),
endContentPadding = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
) {
LazyVerticalGrid(
columns = columns,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalArrangement = horizontalArrangement,
userScrollEnabled = userScrollEnabled,
content = content,
)
}
} else {
LazyVerticalGrid(
columns = columns,
modifier = modifier,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalArrangement = horizontalArrangement,
userScrollEnabled = userScrollEnabled,
content = content,
)
}
}
@@ -26,7 +26,6 @@ import androidx.compose.material3.Text
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.TopAppBarScrollBehavior
import androidx.compose.material3.animateFloatingActionButton import androidx.compose.material3.animateFloatingActionButton
import androidx.compose.material3.rememberTooltipState import androidx.compose.material3.rememberTooltipState
@@ -40,6 +39,7 @@ import androidx.compose.ui.zIndex
import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.HazeState
import io.legado.app.ui.theme.responsiveHazeEffect import io.legado.app.ui.theme.responsiveHazeEffect
import io.legado.app.ui.theme.responsiveHazeSource import io.legado.app.ui.theme.responsiveHazeSource
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
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.topbar.DynamicTopAppBar import io.legado.app.ui.widget.components.topbar.DynamicTopAppBar
@@ -83,7 +83,7 @@ fun <T> ListScaffold(
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
content: @Composable (PaddingValues) -> Unit content: @Composable (PaddingValues) -> Unit
) { ) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
val hazeState = remember { HazeState() } val hazeState = remember { HazeState() }
Scaffold( Scaffold(
@@ -1,8 +1,10 @@
package io.legado.app.ui.widget.components.modalBottomSheet package io.legado.app.ui.widget.components.modalBottomSheet
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.layout.Column 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.heightIn
import androidx.compose.material3.ColorScheme import androidx.compose.material3.ColorScheme
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
@@ -16,6 +18,8 @@ import androidx.compose.material3.rememberModalBottomSheetState
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.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import io.legado.app.ui.theme.rememberOpaqueColorScheme import io.legado.app.ui.theme.rememberOpaqueColorScheme
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@@ -30,6 +34,10 @@ fun GlassModalBottomSheet(
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
val colorScheme = rememberOpaqueColorScheme() val colorScheme = rememberOpaqueColorScheme()
val density = LocalDensity.current
val maxHeight = with(density) {
LocalWindowInfo.current.containerSize.height.toDp() * 0.8f
}
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@@ -46,6 +54,8 @@ fun GlassModalBottomSheet(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(max = maxHeight)
.animateContentSize()
.then(modifier), .then(modifier),
content = content content = content
) )
@@ -1,9 +1,9 @@
package io.legado.app.ui.widget.components.settingItem package io.legado.app.ui.widget.components.settingItem
import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import io.legado.app.ui.widget.components.IconSwitch
@Composable @Composable
fun SwitchSettingItem( fun SwitchSettingItem(
@@ -22,7 +22,7 @@ fun SwitchSettingItem(
color = color, color = color,
onClick = { if (enabled) onCheckedChange(!checked) }, onClick = { if (enabled) onCheckedChange(!checked) },
trailingContent = { trailingContent = {
Switch( IconSwitch(
checked = checked, checked = checked,
onCheckedChange = onCheckedChange, onCheckedChange = onCheckedChange,
enabled = enabled enabled = enabled