组件优化
This commit is contained in:
@@ -69,7 +69,10 @@
|
|||||||
"Bash(Select-String -Pattern \"FAILED|error:\")",
|
"Bash(Select-String -Pattern \"FAILED|error:\")",
|
||||||
"Bash(Select-Object -First 5)",
|
"Bash(Select-Object -First 5)",
|
||||||
"Bash(Select-String -NotMatch \"^\\\\s*$\")",
|
"Bash(Select-String -NotMatch \"^\\\\s*$\")",
|
||||||
"Bash(Select-Object -Last 20)"
|
"Bash(Select-Object -Last 20)",
|
||||||
|
"WebFetch(domain:compose-miuix-ui.github.io)",
|
||||||
|
"Bash(Select-Object -Last 60)",
|
||||||
|
"PowerShell(.\\\\gradlew.bat :app:compileAppDebugKotlin 2>&1 | Select-Object -Last 60)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ object PreferKey {
|
|||||||
const val showBookIntro = "showBookIntro"
|
const val showBookIntro = "showBookIntro"
|
||||||
const val bookshelfShowIntro = "bookshelfShowIntro"
|
const val bookshelfShowIntro = "bookshelfShowIntro"
|
||||||
const val bookshelfShowTag = "bookshelfShowTag"
|
const val bookshelfShowTag = "bookshelfShowTag"
|
||||||
|
const val bookshelfShowLatestChapter = "bookshelfShowLatestChapter"
|
||||||
const val bookshelfIntroMaxLines = "bookshelfIntroMaxLines"
|
const val bookshelfIntroMaxLines = "bookshelfIntroMaxLines"
|
||||||
const val sliderVibrator = "sliderVibrator"
|
const val sliderVibrator = "sliderVibrator"
|
||||||
const val selectVibrator = "selectVibrator"
|
const val selectVibrator = "selectVibrator"
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
|
||||||
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
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -136,7 +134,6 @@ fun ExploreShowScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val isRefreshing by viewModel.isRefreshing.collectAsState()
|
val isRefreshing by viewModel.isRefreshing.collectAsState()
|
||||||
val pullToRefreshState = rememberPullToRefreshState()
|
|
||||||
|
|
||||||
val hazeState = remember { HazeState() }
|
val hazeState = remember { HazeState() }
|
||||||
val shouldLoadMoreList = remember {
|
val shouldLoadMoreList = remember {
|
||||||
@@ -409,20 +406,10 @@ fun ExploreShowScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
PullToRefreshBox(
|
AppPullToRefresh(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
isRefreshing = isRefreshing,
|
isRefreshing = isRefreshing,
|
||||||
state = pullToRefreshState,
|
|
||||||
onRefresh = { viewModel.loadMore(isRefresh = true) },
|
onRefresh = { viewModel.loadMore(isRefresh = true) },
|
||||||
indicator = {
|
|
||||||
PullToRefreshDefaults.LoadingIndicator(
|
|
||||||
state = pullToRefreshState,
|
|
||||||
isRefreshing = isRefreshing,
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.TopCenter)
|
|
||||||
.padding(top = paddingValues.calculateTopPadding())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
Crossfade(
|
Crossfade(
|
||||||
targetState = isGridMode,
|
targetState = isGridMode,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
@@ -35,8 +36,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||||
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
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -178,31 +178,29 @@ private fun ImportBookContent(
|
|||||||
onSelectInvert = onSelectInvert,
|
onSelectInvert = onSelectInvert,
|
||||||
primaryAction = ActionItem(
|
primaryAction = ActionItem(
|
||||||
text = stringResource(R.string.add_to_bookshelf),
|
text = stringResource(R.string.add_to_bookshelf),
|
||||||
icon = { Icon(Icons.Default.CloudDownload, null) },
|
icon = Icons.Default.CloudDownload,
|
||||||
onClick = onAddToBookshelf
|
onClick = onAddToBookshelf
|
||||||
),
|
),
|
||||||
secondaryActions = listOf(
|
secondaryActions = listOf(
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = stringResource(R.string.delete),
|
text = stringResource(R.string.delete),
|
||||||
icon = { Icon(Icons.Default.Delete, null) },
|
icon = Icons.Default.Delete,
|
||||||
onClick = onDeleteSelection
|
onClick = onDeleteSelection
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
onAddClick = null
|
onAddClick = null
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
val refreshState = rememberPullToRefreshState()
|
AppPullToRefresh(
|
||||||
PullToRefreshBox(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues),
|
.padding(paddingValues),
|
||||||
isRefreshing = state.isLoading,
|
isRefreshing = state.isLoading,
|
||||||
state = refreshState,
|
onRefresh = onScanFolder,
|
||||||
onRefresh = onScanFolder
|
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
state.items.isEmpty() && state.isLoading -> {
|
state.items.isEmpty() && state.isLoading -> {
|
||||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
CircularProgressIndicator(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.Center))
|
||||||
}
|
}
|
||||||
|
|
||||||
state.items.isEmpty() -> {
|
state.items.isEmpty() -> {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -44,8 +45,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||||
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
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -84,6 +84,7 @@ import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
|||||||
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.card.SelectionItemCard
|
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||||
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.icon.AppIcon
|
||||||
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.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
@@ -303,7 +304,7 @@ fun RemoteBookScreen(
|
|||||||
onSelectInvert = { viewModel.dispatch(RemoteBookIntent.SelectInvert) },
|
onSelectInvert = { viewModel.dispatch(RemoteBookIntent.SelectInvert) },
|
||||||
primaryAction = ActionItem(
|
primaryAction = ActionItem(
|
||||||
text = "添加至书架",
|
text = "添加至书架",
|
||||||
icon = { Icon(Icons.Default.CloudDownload, null) },
|
icon = Icons.Default.CloudDownload,
|
||||||
onClick = {
|
onClick = {
|
||||||
val selectedBooks = uiState.items
|
val selectedBooks = uiState.items
|
||||||
.filter { it.id in uiState.selectedIds }
|
.filter { it.id in uiState.selectedIds }
|
||||||
@@ -316,18 +317,16 @@ fun RemoteBookScreen(
|
|||||||
),
|
),
|
||||||
onAddClick = null,
|
onAddClick = null,
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
val pullToRefreshState = rememberPullToRefreshState()
|
AppPullToRefresh(
|
||||||
PullToRefreshBox(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues),
|
.padding(paddingValues),
|
||||||
isRefreshing = uiState.isLoading,
|
isRefreshing = uiState.isLoading,
|
||||||
state = pullToRefreshState,
|
onRefresh = { viewModel.dispatch(RemoteBookIntent.Refresh) },
|
||||||
onRefresh = { viewModel.dispatch(RemoteBookIntent.Refresh) }
|
|
||||||
) {
|
) {
|
||||||
if (uiState.items.isEmpty()) {
|
if (uiState.items.isEmpty()) {
|
||||||
if (uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
CircularProgressIndicator(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.Center))
|
||||||
} else {
|
} else {
|
||||||
EmptyMessage(
|
EmptyMessage(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.MediumFlexibleTopAppBar
|
import androidx.compose.material3.MediumFlexibleTopAppBar
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
|
||||||
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
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -155,7 +153,6 @@ private fun BookInfoScreenContent(
|
|||||||
M3GlassScrollBehavior(TopAppBarDefaults.exitUntilCollapsedScrollBehavior())
|
M3GlassScrollBehavior(TopAppBarDefaults.exitUntilCollapsedScrollBehavior())
|
||||||
}
|
}
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val pullState = rememberPullToRefreshState()
|
|
||||||
var showMenu by rememberSaveable { mutableStateOf(false) }
|
var showMenu by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
@@ -194,22 +191,10 @@ private fun BookInfoScreenContent(
|
|||||||
} else {
|
} else {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
BookInfoBackdrop(book)
|
BookInfoBackdrop(book)
|
||||||
PullToRefreshBox(
|
AppPullToRefresh(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = pullState,
|
|
||||||
isRefreshing = state.isTocLoading,
|
isRefreshing = state.isTocLoading,
|
||||||
onRefresh = { onIntent(BookInfoIntent.MenuAction(BookInfoMenuAction.Refresh)) },
|
onRefresh = { onIntent(BookInfoIntent.MenuAction(BookInfoMenuAction.Refresh)) },
|
||||||
indicator = {
|
|
||||||
PullToRefreshDefaults.LoadingIndicator(
|
|
||||||
state = pullState,
|
|
||||||
isRefreshing = state.isTocLoading,
|
|
||||||
containerColor = LegadoTheme.colorScheme.surfaceContainerHigh,
|
|
||||||
color = LegadoTheme.colorScheme.primary,
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.TopCenter)
|
|
||||||
.padding(top = paddingValues.calculateTopPadding())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ 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.divider.PillDivider
|
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||||
import io.legado.app.ui.widget.components.divider.PillHeaderDivider
|
import io.legado.app.ui.widget.components.divider.PillHeaderDivider
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||||
import io.legado.app.ui.widget.components.list.TopFloatingStickyItem
|
import io.legado.app.ui.widget.components.list.TopFloatingStickyItem
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||||
@@ -244,17 +245,17 @@ fun TocScreen(
|
|||||||
listOf(
|
listOf(
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = "反选",
|
text = "反选",
|
||||||
icon = { Icon(Icons.Default.Refresh, contentDescription = null) },
|
icon = Icons.Default.Refresh,
|
||||||
onClick = { viewModel.invertSelection() }
|
onClick = { viewModel.invertSelection() }
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = "选择后续",
|
text = "选择后续",
|
||||||
icon = { Icon(Icons.Default.ExpandMore, contentDescription = null) },
|
icon = Icons.Default.ExpandMore,
|
||||||
onClick = { viewModel.selectFromLast() }
|
onClick = { viewModel.selectFromLast() }
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = "添加书签",
|
text = "添加书签",
|
||||||
icon = { Icon(Icons.Default.BookmarkAdd, contentDescription = null) },
|
icon = Icons.Default.BookmarkAdd,
|
||||||
onClick = { viewModel.addBookmarksForSelected() }
|
onClick = { viewModel.addBookmarksForSelected() }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -577,7 +578,7 @@ fun TocScreen(
|
|||||||
onSelectInvert = { viewModel.invertSelection() },
|
onSelectInvert = { viewModel.invertSelection() },
|
||||||
primaryAction = ActionItem(
|
primaryAction = ActionItem(
|
||||||
text = "下载已选 (${state.selectedIds.size})",
|
text = "下载已选 (${state.selectedIds.size})",
|
||||||
icon = { Icon(Icons.Default.Download, null) },
|
icon = Icons.Default.Download,
|
||||||
onClick = { viewModel.downloadSelected() }
|
onClick = { viewModel.downloadSelected() }
|
||||||
),
|
),
|
||||||
secondaryActions = selectionSecondaryActions
|
secondaryActions = selectionSecondaryActions
|
||||||
|
|||||||
@@ -91,6 +91,13 @@ object BookshelfConfig {
|
|||||||
var bookshelfShowTag by _bookshelfShowTag
|
var bookshelfShowTag by _bookshelfShowTag
|
||||||
val bookshelfShowTagState: State<Boolean> get() = _bookshelfShowTag.state
|
val bookshelfShowTagState: State<Boolean> get() = _bookshelfShowTag.state
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示最新章节
|
||||||
|
*/
|
||||||
|
private val _bookshelfShowLatestChapter = prefStateDelegate(PreferKey.bookshelfShowLatestChapter, true)
|
||||||
|
var bookshelfShowLatestChapter by _bookshelfShowLatestChapter
|
||||||
|
val bookshelfShowLatestChapterState: State<Boolean> get() = _bookshelfShowLatestChapter.state
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 列表模式下简介显示行数 (0为显示全部)
|
* 列表模式下简介显示行数 (0为显示全部)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.horizontalScroll
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
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.Spacer
|
||||||
@@ -20,6 +21,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
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.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||||
@@ -77,6 +79,7 @@ fun BookshelfItem(
|
|||||||
desc: String? = null,
|
desc: String? = null,
|
||||||
descMaxLines: Int = 1,
|
descMaxLines: Int = 1,
|
||||||
extra: @Composable (RowScope.() -> Unit)? = null,
|
extra: @Composable (RowScope.() -> Unit)? = null,
|
||||||
|
columnContent: @Composable (ColumnScope.() -> Unit)? = null,
|
||||||
bottomContent: @Composable (() -> Unit)? = null,
|
bottomContent: @Composable (() -> Unit)? = null,
|
||||||
titleSmallFont: Boolean = false,
|
titleSmallFont: Boolean = false,
|
||||||
titleCenter: Boolean = true,
|
titleCenter: Boolean = true,
|
||||||
@@ -185,6 +188,7 @@ fun BookshelfItem(
|
|||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.align(Alignment.Top)
|
||||||
.width(if (!isCompact) 80.dp else 56.dp)
|
.width(if (!isCompact) 80.dp else 56.dp)
|
||||||
.padding(end = 8.dp)
|
.padding(end = 8.dp)
|
||||||
) {
|
) {
|
||||||
@@ -205,7 +209,7 @@ fun BookshelfItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f).padding(top = 4.dp, bottom = 4.dp, end = 8.dp),
|
modifier = Modifier.weight(1f).padding(top = 8.dp, bottom = 8.dp, end = 8.dp),
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -257,6 +261,7 @@ fun BookshelfItem(
|
|||||||
content = it
|
content = it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
columnContent?.invoke(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bottomContent?.invoke()
|
bottomContent?.invoke()
|
||||||
@@ -438,12 +443,11 @@ fun BookGroupItemList(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val firstBookName = previewBooks.firstOrNull()?.name
|
val firstBookName = previewBooks.firstOrNull()?.name
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
|
||||||
val descAnnotated = if (firstBookName != null) {
|
val descAnnotated = if (firstBookName != null) {
|
||||||
buildAnnotatedString {
|
buildAnnotatedString {
|
||||||
append("最近阅读:")
|
append("最近阅读:")
|
||||||
withStyle(SpanStyle(color = primaryColor, fontWeight = FontWeight.Medium)) {
|
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
||||||
append("《$firstBookName》")
|
append(firstBookName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -455,7 +459,6 @@ fun BookGroupItemList(
|
|||||||
isCompact = BookshelfConfig.bookshelfGroupListStyle == 1 || 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,
|
|
||||||
subTitle = countText,
|
subTitle = countText,
|
||||||
descAnnotated = descAnnotated,
|
descAnnotated = descAnnotated,
|
||||||
titleSmallFont = titleSmallFont,
|
titleSmallFont = titleSmallFont,
|
||||||
@@ -649,37 +652,38 @@ fun BookItem(
|
|||||||
book.author
|
book.author
|
||||||
},
|
},
|
||||||
desc = book.durChapterTitle ?: "",
|
desc = book.durChapterTitle ?: "",
|
||||||
bottomContent = if (layoutMode == 0 && BookshelfConfig.showBookIntro) {
|
columnContent = if (layoutMode == 0 && !isCompact && BookshelfConfig.showBookIntro) {
|
||||||
{
|
{
|
||||||
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 (BookshelfConfig.bookshelfShowTag && !kindList.isNullOrEmpty()) {
|
if (BookshelfConfig.bookshelfShowTag && !kindList.isNullOrEmpty()) {
|
||||||
FlowRow(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 4.dp, vertical = 4.dp),
|
.padding(vertical = 4.dp)
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
.horizontalScroll(rememberScrollState()),
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
verticalAlignment = Alignment.CenterVertically
|
||||||
kindList.forEachIndexed { index, label ->
|
) {
|
||||||
val colorPair = if (customTagColors.isNotEmpty()) {
|
kindList.forEachIndexed { index, label ->
|
||||||
customTagColors[index % customTagColors.size]
|
val colorPair = if (customTagColors.isNotEmpty()) {
|
||||||
} else {
|
customTagColors[index % customTagColors.size]
|
||||||
null
|
} else {
|
||||||
}
|
null
|
||||||
TextCard(
|
|
||||||
text = label,
|
|
||||||
backgroundColor = if (colorPair != null && colorPair.bgColor != 0) Color(colorPair.bgColor) else LegadoTheme.colorScheme.surfaceContainerHighest,
|
|
||||||
contentColor = if (colorPair != null && colorPair.textColor != 0) Color(colorPair.textColor) else LegadoTheme.colorScheme.primary,
|
|
||||||
cornerRadius = 4.dp,
|
|
||||||
horizontalPadding = 6.dp,
|
|
||||||
verticalPadding = 2.dp,
|
|
||||||
textStyle = LegadoTheme.typography.labelSmall
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
TextCard(
|
||||||
|
text = label,
|
||||||
|
backgroundColor = if (colorPair != null && colorPair.bgColor != 0) Color(colorPair.bgColor) else LegadoTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
contentColor = if (colorPair != null && colorPair.textColor != 0) Color(colorPair.textColor) else LegadoTheme.colorScheme.primary.copy(alpha = 0.8f),
|
||||||
|
cornerRadius = 4.dp,
|
||||||
|
horizontalPadding = 4.dp,
|
||||||
|
verticalPadding = 2.dp,
|
||||||
|
textStyle = LegadoTheme.typography.labelSmallEmphasized
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (BookshelfConfig.bookshelfShowIntro && 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(
|
||||||
@@ -690,29 +694,32 @@ fun BookItem(
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 4.dp, vertical = 4.dp)
|
.padding(vertical = 4.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else null,
|
} else null,
|
||||||
extra = {
|
bottomContent = null,
|
||||||
if (BookshelfConfig.showLastUpdateTime && !book.isLocal) {
|
extra = if (layoutMode == 0 && !isCompact && BookshelfConfig.showBookIntro && BookshelfConfig.bookshelfShowLatestChapter) {
|
||||||
|
{
|
||||||
|
if (BookshelfConfig.showLastUpdateTime && !book.isLocal) {
|
||||||
|
AppText(
|
||||||
|
text = book.latestChapterTime.toTimeAgo(),
|
||||||
|
style = LegadoTheme.typography.labelSmallEmphasized,
|
||||||
|
color = LegadoTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
||||||
|
modifier = Modifier.padding(end = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
AppText(
|
AppText(
|
||||||
text = book.latestChapterTime.toTimeAgo(),
|
text = book.latestChapterTitle ?: "",
|
||||||
style = LegadoTheme.typography.labelSmallEmphasized,
|
style = LegadoTheme.typography.labelSmallEmphasized,
|
||||||
color = LegadoTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
color = LegadoTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
||||||
modifier = Modifier.padding(end = 4.dp)
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AppText(
|
} else null,
|
||||||
text = book.latestChapterTitle ?: "",
|
|
||||||
style = LegadoTheme.typography.labelSmallEmphasized,
|
|
||||||
color = LegadoTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
titleSmallFont = titleSmallFont,
|
titleSmallFont = titleSmallFont,
|
||||||
titleCenter = titleCenter,
|
titleCenter = titleCenter,
|
||||||
titleMaxLines = titleMaxLines,
|
titleMaxLines = titleMaxLines,
|
||||||
|
|||||||
@@ -288,6 +288,13 @@ fun BookshelfConfigSheet(
|
|||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
|
CompactSwitchSettingItem(
|
||||||
|
title = "显示最新章节",
|
||||||
|
checked = BookshelfConfig.bookshelfShowLatestChapter,
|
||||||
|
color = LegadoTheme.colorScheme.surface,
|
||||||
|
onCheckedChange = { BookshelfConfig.bookshelfShowLatestChapter = it }
|
||||||
|
)
|
||||||
|
|
||||||
CompactSwitchSettingItem(
|
CompactSwitchSettingItem(
|
||||||
title = "显示简介",
|
title = "显示简介",
|
||||||
checked = BookshelfConfig.bookshelfShowIntro,
|
checked = BookshelfConfig.bookshelfShowIntro,
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ 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.divider.PillHeaderDivider
|
import io.legado.app.ui.widget.components.divider.PillHeaderDivider
|
||||||
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.icon.AppIcon
|
||||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||||
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.lazylist.FastScrollLazyVerticalGrid
|
||||||
@@ -1061,7 +1062,7 @@ fun BookshelfScreen(
|
|||||||
onSelectInvert = { viewModel.invertVisibleSelection() },
|
onSelectInvert = { viewModel.invertVisibleSelection() },
|
||||||
primaryAction = ActionItem(
|
primaryAction = ActionItem(
|
||||||
text = stringResource(R.string.action_download),
|
text = stringResource(R.string.action_download),
|
||||||
icon = { Icon(Icons.Default.Download, contentDescription = null) },
|
icon = Icons.Default.Download,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (selectedBookUrls.isNotEmpty()) {
|
if (selectedBookUrls.isNotEmpty()) {
|
||||||
viewModel.showOverlay(BookshelfOverlay.BatchDownloadConfirmDialog)
|
viewModel.showOverlay(BookshelfOverlay.BatchDownloadConfirmDialog)
|
||||||
@@ -1071,7 +1072,7 @@ fun BookshelfScreen(
|
|||||||
secondaryActions = listOf(
|
secondaryActions = listOf(
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = stringResource(R.string.move_to_group),
|
text = stringResource(R.string.move_to_group),
|
||||||
icon = { Icon(Icons.Default.Bookmarks, contentDescription = null) },
|
icon = Icons.Default.Bookmarks,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (selectedBookUrls.isNotEmpty()) {
|
if (selectedBookUrls.isNotEmpty()) {
|
||||||
viewModel.showOverlay(BookshelfOverlay.GroupSelectSheet)
|
viewModel.showOverlay(BookshelfOverlay.GroupSelectSheet)
|
||||||
|
|||||||
@@ -710,7 +710,9 @@ class BookshelfViewModel(
|
|||||||
fun refreshBooks(books: List<BookShelfItem>) {
|
fun refreshBooks(books: List<BookShelfItem>) {
|
||||||
if (isRefreshingFlow.value) return
|
if (isRefreshingFlow.value) return
|
||||||
isRefreshingFlow.value = true
|
isRefreshingFlow.value = true
|
||||||
enqueueTocUpdate(books, resetRefreshWhenIdle = true)
|
val limit = BookshelfConfig.bookshelfRefreshingLimit
|
||||||
|
val list = if (limit > 0) books.take(limit) else books
|
||||||
|
enqueueTocUpdate(list, resetRefreshWhenIdle = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startDraggingBooks(books: List<BookShelfItem>) {
|
fun startDraggingBooks(books: List<BookShelfItem>) {
|
||||||
@@ -761,7 +763,9 @@ class BookshelfViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upToc(books: List<BookShelfItem>) {
|
fun upToc(books: List<BookShelfItem>) {
|
||||||
enqueueTocUpdate(books, resetRefreshWhenIdle = false)
|
val limit = BookshelfConfig.bookshelfRefreshingLimit
|
||||||
|
val list = if (limit > 0) books.take(limit) else books
|
||||||
|
enqueueTocUpdate(list, resetRefreshWhenIdle = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enqueueTocUpdate(
|
private fun enqueueTocUpdate(
|
||||||
|
|||||||
@@ -118,37 +118,12 @@ fun ExploreScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val expandedHeader = remember(uiState.expandedId, uiState.listItems) {
|
val stickyHeaderSource by remember(uiState.listItems, uiState.items) {
|
||||||
val expandedId = uiState.expandedId ?: return@remember null
|
|
||||||
val headerIndex = uiState.listItems.indexOfFirst {
|
|
||||||
it is ExploreListItem.Header && it.source.bookSourceUrl == expandedId
|
|
||||||
}
|
|
||||||
val headerItem = uiState.listItems.getOrNull(headerIndex) as? ExploreListItem.Header
|
|
||||||
if (headerItem != null) {
|
|
||||||
ExpandedExploreHeader(
|
|
||||||
source = headerItem.source,
|
|
||||||
headerIndex = headerIndex,
|
|
||||||
contentRowCount = uiState.listItems.count {
|
|
||||||
it is ExploreListItem.KindRow && it.sourceUrl == expandedId
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(expandedHeader?.headerIndex) {
|
|
||||||
expandedHeader?.let { listState.animateScrollToItem(it.headerIndex) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val stickyHeaderSource by remember(expandedHeader) {
|
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
val header = expandedHeader ?: return@derivedStateOf null
|
val firstIndex = listState.firstVisibleItemIndex
|
||||||
val lastContentIndex = header.headerIndex + header.contentRowCount
|
val item = uiState.listItems.getOrNull(firstIndex)
|
||||||
val firstVisible = listState.firstVisibleItemIndex
|
if (item is ExploreListItem.KindRow) {
|
||||||
|
uiState.items.find { it.bookSourceUrl == item.sourceUrl }
|
||||||
if (firstVisible in (header.headerIndex + 1)..lastContentIndex) {
|
|
||||||
header.source
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -292,8 +267,9 @@ fun ExploreScreen(
|
|||||||
verticalPadding = 8.dp,
|
verticalPadding = 8.dp,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val index =
|
val index = uiState.listItems.indexOfFirst {
|
||||||
uiState.items.indexOfFirst { it.bookSourceUrl == item.bookSourceUrl }
|
it is ExploreListItem.Header && it.source.bookSourceUrl == item.bookSourceUrl
|
||||||
|
}
|
||||||
if (index >= 0) listState.animateScrollToItem(index)
|
if (index >= 0) listState.animateScrollToItem(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,11 +293,6 @@ fun ExploreScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ExpandedExploreHeader(
|
|
||||||
val source: BookSourcePart,
|
|
||||||
val headerIndex: Int,
|
|
||||||
val contentRowCount: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridS
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||||
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
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -132,17 +131,15 @@ fun RssArticlesPage(
|
|||||||
loadState.errorMessage?.takeIf { it.isNotBlank() }?.let(context::toastOnUi)
|
loadState.errorMessage?.takeIf { it.isNotBlank() }?.let(context::toastOnUi)
|
||||||
}
|
}
|
||||||
|
|
||||||
val refreshState = rememberPullToRefreshState()
|
|
||||||
val contentPadding = adaptiveContentPadding(
|
val contentPadding = adaptiveContentPadding(
|
||||||
top = paddingValues.calculateTopPadding(),
|
top = paddingValues.calculateTopPadding(),
|
||||||
bottom = 120.dp
|
bottom = 120.dp
|
||||||
)
|
)
|
||||||
|
|
||||||
PullToRefreshBox(
|
AppPullToRefresh(
|
||||||
isRefreshing = loadState.isRefreshing,
|
isRefreshing = loadState.isRefreshing,
|
||||||
onRefresh = { rssSource?.let(viewModel::loadArticles) },
|
onRefresh = { rssSource?.let(viewModel::loadArticles) },
|
||||||
state = refreshState,
|
modifier = modifier.fillMaxSize(),
|
||||||
modifier = modifier.fillMaxSize()
|
|
||||||
) {
|
) {
|
||||||
when (layout) {
|
when (layout) {
|
||||||
RssArticleLayout.List, RssArticleLayout.LargeCard -> {
|
RssArticleLayout.List, RssArticleLayout.LargeCard -> {
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import top.yukonga.miuix.kmp.basic.PullToRefresh as MiuixPullToRefresh
|
||||||
|
import top.yukonga.miuix.kmp.basic.rememberPullToRefreshState as miuixRememberPullToRefreshState
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun AppPullToRefresh(
|
||||||
|
isRefreshing: Boolean,
|
||||||
|
onRefresh: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
if (enabled) {
|
||||||
|
MiuixPullToRefresh(
|
||||||
|
isRefreshing = isRefreshing,
|
||||||
|
onRefresh = onRefresh,
|
||||||
|
modifier = modifier,
|
||||||
|
pullToRefreshState = miuixRememberPullToRefreshState(),
|
||||||
|
refreshTexts = listOf(
|
||||||
|
"下拉刷新",
|
||||||
|
"松开刷新",
|
||||||
|
"正在刷新",
|
||||||
|
"刷新成功"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Box(modifier) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (enabled) {
|
||||||
|
androidx.compose.material3.pulltorefresh.PullToRefreshBox(
|
||||||
|
isRefreshing = isRefreshing,
|
||||||
|
onRefresh = onRefresh,
|
||||||
|
modifier = modifier,
|
||||||
|
state = androidx.compose.material3.pulltorefresh.rememberPullToRefreshState(),
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Box(modifier) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ExpandMore
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material.icons.filled.SelectAll
|
import androidx.compose.material.icons.filled.SelectAll
|
||||||
@@ -12,7 +16,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.FilledIconButton
|
import androidx.compose.material3.FilledIconButton
|
||||||
import androidx.compose.material3.HorizontalFloatingToolbar
|
import androidx.compose.material3.HorizontalFloatingToolbar
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.PlainTooltip
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.TooltipAnchorPosition
|
import androidx.compose.material3.TooltipAnchorPosition
|
||||||
@@ -24,9 +27,20 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
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.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||||
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
import top.yukonga.miuix.kmp.basic.FloatingToolbar as MiuixFloatingToolbar
|
||||||
|
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||||
|
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
|
||||||
data class SelectionActions(
|
data class SelectionActions(
|
||||||
val primaryAction: ActionItem,
|
val primaryAction: ActionItem,
|
||||||
@@ -38,7 +52,7 @@ data class SelectionActions(
|
|||||||
|
|
||||||
data class ActionItem(
|
data class ActionItem(
|
||||||
val text: String,
|
val text: String,
|
||||||
val icon: @Composable (() -> Unit)? = null,
|
val icon: ImageVector = Icons.Default.ExpandMore,
|
||||||
val onClick: () -> Unit
|
val onClick: () -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,66 +66,129 @@ fun SelectionBottomBar(
|
|||||||
secondaryActions: List<ActionItem>
|
secondaryActions: List<ActionItem>
|
||||||
) {
|
) {
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
|
|
||||||
HorizontalFloatingToolbar(
|
if (isMiuix) {
|
||||||
modifier = modifier,
|
MiuixFloatingToolbar(
|
||||||
expanded = true,
|
modifier = modifier
|
||||||
leadingContent = {
|
) {
|
||||||
IconButton(onClick = onSelectAll) {
|
Row(
|
||||||
Icon(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
imageVector = Icons.Default.SelectAll,
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
contentDescription = "Select All"
|
modifier = Modifier.padding(8.dp)
|
||||||
)
|
) {
|
||||||
}
|
MiuixIconButton(onClick = onSelectAll) {
|
||||||
IconButton(onClick = onSelectInvert) {
|
MiuixIcon(
|
||||||
Icon(
|
imageVector = Icons.Default.SelectAll,
|
||||||
imageVector = Icons.Default.Refresh,
|
contentDescription = "Select All"
|
||||||
contentDescription = "Invert Selection"
|
)
|
||||||
)
|
}
|
||||||
}
|
MiuixIconButton(onClick = onSelectInvert) {
|
||||||
},
|
MiuixIcon(
|
||||||
trailingContent = {
|
imageVector = Icons.Default.Refresh,
|
||||||
if (secondaryActions.isNotEmpty()) {
|
contentDescription = "Invert Selection"
|
||||||
Box {
|
)
|
||||||
IconButton(onClick = { showMenu = true }) {
|
}
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.MoreVert,
|
MiuixIconButton(
|
||||||
contentDescription = "More actions"
|
onClick = primaryAction.onClick,
|
||||||
)
|
backgroundColor = MiuixTheme.colorScheme.primary,
|
||||||
}
|
minWidth = 64.dp
|
||||||
DropdownMenu(
|
) {
|
||||||
expanded = showMenu,
|
MiuixIcon(
|
||||||
onDismissRequest = { showMenu = false }
|
imageVector = primaryAction.icon,
|
||||||
) {
|
contentDescription = null,
|
||||||
secondaryActions.forEach { action ->
|
tint = MiuixTheme.colorScheme.onPrimary
|
||||||
DropdownMenuItem(
|
)
|
||||||
text = { AppText(action.text) },
|
}
|
||||||
leadingIcon = action.icon,
|
|
||||||
onClick = {
|
if (secondaryActions.isNotEmpty()) {
|
||||||
action.onClick()
|
Box {
|
||||||
showMenu = false
|
MiuixIconButton(onClick = { showMenu = true }) {
|
||||||
}
|
MiuixIcon(
|
||||||
|
imageVector = Icons.Default.MoreVert,
|
||||||
|
contentDescription = "More actions"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
RoundDropdownMenu(
|
||||||
|
expanded = showMenu,
|
||||||
|
onDismissRequest = { showMenu = false }
|
||||||
|
) {
|
||||||
|
secondaryActions.forEach { action ->
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = action.text,
|
||||||
|
onClick = {
|
||||||
|
action.onClick()
|
||||||
|
showMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
content = {
|
} else {
|
||||||
TooltipBox(
|
HorizontalFloatingToolbar(
|
||||||
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(
|
modifier = modifier,
|
||||||
TooltipAnchorPosition.Above
|
expanded = true,
|
||||||
),
|
leadingContent = {
|
||||||
tooltip = { PlainTooltip { AppText(primaryAction.text) } },
|
IconButton(onClick = onSelectAll) {
|
||||||
state = rememberTooltipState(),
|
AppIcon(
|
||||||
) {
|
imageVector = Icons.Default.SelectAll,
|
||||||
FilledIconButton(
|
contentDescription = "Select All"
|
||||||
modifier = Modifier.width(64.dp),
|
)
|
||||||
onClick = primaryAction.onClick,
|
}
|
||||||
|
IconButton(onClick = onSelectInvert) {
|
||||||
|
AppIcon(
|
||||||
|
imageVector = Icons.Default.Refresh,
|
||||||
|
contentDescription = "Invert Selection"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailingContent = {
|
||||||
|
if (secondaryActions.isNotEmpty()) {
|
||||||
|
Box {
|
||||||
|
IconButton(onClick = { showMenu = true }) {
|
||||||
|
AppIcon(
|
||||||
|
imageVector = Icons.Default.MoreVert,
|
||||||
|
contentDescription = "More actions"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = showMenu,
|
||||||
|
onDismissRequest = { showMenu = false }
|
||||||
|
) {
|
||||||
|
secondaryActions.forEach { action ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { AppText(action.text) },
|
||||||
|
leadingIcon = action.icon?.let { { AppIcon(imageVector = it, contentDescription = null) } },
|
||||||
|
onClick = {
|
||||||
|
action.onClick()
|
||||||
|
showMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(
|
||||||
|
TooltipAnchorPosition.Above
|
||||||
|
),
|
||||||
|
tooltip = { PlainTooltip { AppText(primaryAction.text) } },
|
||||||
|
state = rememberTooltipState(),
|
||||||
) {
|
) {
|
||||||
primaryAction.icon?.invoke()
|
FilledIconButton(
|
||||||
|
modifier = Modifier.width(64.dp),
|
||||||
|
onClick = primaryAction.onClick,
|
||||||
|
) {
|
||||||
|
AppIcon(imageVector = primaryAction.icon, contentDescription = null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ fun <T> RuleListScaffold(
|
|||||||
onSelectInvert = onSelectInvert,
|
onSelectInvert = onSelectInvert,
|
||||||
primaryAction = ActionItem(
|
primaryAction = ActionItem(
|
||||||
text = stringResource(R.string.delete),
|
text = stringResource(R.string.delete),
|
||||||
icon = { Icon(Icons.Default.Delete, null) },
|
icon = Icons.Default.Delete,
|
||||||
onClick = { showDeleteConfirmDialog = true }
|
onClick = { showDeleteConfirmDialog = true }
|
||||||
),
|
),
|
||||||
secondaryActions = selectionSecondaryActions
|
secondaryActions = selectionSecondaryActions
|
||||||
|
|||||||
Reference in New Issue
Block a user