[优化] 优化界面
This commit is contained in:
@@ -160,6 +160,12 @@ interface ReadRecordDao {
|
||||
@Query("SELECT * FROM readRecordSession WHERE deviceId = :deviceId AND bookName = :bookName AND bookAuthor = :bookAuthor")
|
||||
suspend fun getSessionsByBook(deviceId: String, bookName: String, bookAuthor: String): List<ReadRecordSession>
|
||||
|
||||
@Query("SELECT * FROM readRecordSession WHERE deviceId = :deviceId AND bookName = :bookName AND bookAuthor = :bookAuthor ORDER BY startTime DESC")
|
||||
fun getSessionsByBookFlow(deviceId: String, bookName: String, bookAuthor: String): Flow<List<ReadRecordSession>>
|
||||
|
||||
@Query("SELECT readTime FROM readRecord WHERE deviceId = :deviceId AND bookName = :bookName AND bookAuthor = :bookAuthor")
|
||||
fun getReadTimeFlow(deviceId: String, bookName: String, bookAuthor: String): Flow<Long?>
|
||||
|
||||
@Delete
|
||||
suspend fun deleteDetail(detail: ReadRecordDetail)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.legado.app.data.dao.ReadRecordDao
|
||||
import io.legado.app.data.entities.readRecord.ReadRecord
|
||||
import io.legado.app.data.entities.readRecord.ReadRecordDetail
|
||||
import io.legado.app.data.entities.readRecord.ReadRecordSession
|
||||
import io.legado.app.data.entities.readRecord.ReadRecordTimelineDay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.util.Date
|
||||
@@ -51,6 +52,27 @@ class ReadRecordRepository(
|
||||
return dao.getAllSessions(getCurrentDeviceId())
|
||||
}
|
||||
|
||||
fun getBookSessions(bookName: String, bookAuthor: String): Flow<List<ReadRecordSession>> {
|
||||
return dao.getSessionsByBookFlow(getCurrentDeviceId(), bookName, bookAuthor)
|
||||
}
|
||||
|
||||
fun getBookTimelineDays(bookName: String, bookAuthor: String): Flow<List<ReadRecordTimelineDay>> {
|
||||
return getBookSessions(bookName, bookAuthor).map { sessions ->
|
||||
sessions.groupBy { DateUtil.format(Date(it.startTime), "yyyy-MM-dd") }
|
||||
.toSortedMap(compareByDescending { it })
|
||||
.map { (date, daySessions) ->
|
||||
ReadRecordTimelineDay(
|
||||
date = date,
|
||||
sessions = daySessions.sortedByDescending { it.startTime }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBookReadTime(bookName: String, bookAuthor: String): Flow<Long> {
|
||||
return dao.getReadTimeFlow(getCurrentDeviceId(), bookName, bookAuthor).map { it ?: 0L }
|
||||
}
|
||||
|
||||
suspend fun getMergeCandidates(targetRecord: ReadRecord): List<ReadRecord> {
|
||||
return dao.getReadRecordsByNameExcludingAuthor(
|
||||
targetRecord.deviceId,
|
||||
|
||||
@@ -53,7 +53,7 @@ import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet
|
||||
import io.legado.app.ui.widget.components.bookmark.BookmarkItem
|
||||
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||
@@ -190,7 +190,7 @@ fun AllBookmarkScreen(
|
||||
enter = expandVertically() + fadeIn(),
|
||||
exit = shrinkVertically() + fadeOut()
|
||||
) {
|
||||
SearchBarSection(
|
||||
SearchBar(
|
||||
query = searchText,
|
||||
onQueryChange = { viewModel.onSearchQueryChanged(it) },
|
||||
placeholder = "搜索...",
|
||||
@@ -258,7 +258,7 @@ fun AllBookmarkScreen(
|
||||
.animateItem()
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
BookmarkGroupHeaderContent(
|
||||
|
||||
@@ -10,22 +10,16 @@ import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
@@ -35,21 +29,17 @@ import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.FormatListBulleted
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.FilterList
|
||||
import androidx.compose.material.icons.filled.GridView
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material.icons.outlined.FilterAlt
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
@@ -63,14 +53,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
@@ -81,12 +68,11 @@ import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.theme.responsiveHazeEffect
|
||||
import io.legado.app.ui.theme.responsiveHazeSource
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.button.AnimatedTextButton
|
||||
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||
import io.legado.app.ui.widget.components.card.TextCard
|
||||
import io.legado.app.ui.widget.components.cover.Cover
|
||||
import io.legado.app.ui.widget.components.explore.calculateExploreKindRows
|
||||
import io.legado.app.ui.widget.components.explore.ExploreKindMultiTypeItem
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
@@ -254,7 +240,7 @@ fun ExploreShowScreen(
|
||||
|
||||
var kindQuery by remember { mutableStateOf("") }
|
||||
|
||||
SearchBarSection(
|
||||
SearchBar(
|
||||
query = kindQuery,
|
||||
backgroundColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.5f),
|
||||
onQueryChange = { kindQuery = it },
|
||||
|
||||
@@ -564,7 +564,7 @@ private fun PathNavigationBar(
|
||||
GlassCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
shape = MaterialTheme.shapes.medium
|
||||
cornerRadius = 12.dp,
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
@@ -74,7 +74,7 @@ import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.widget.CollapsibleHeader
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.AppIconButton
|
||||
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||
@@ -102,6 +102,7 @@ import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||
import io.legado.app.utils.formatReadDuration
|
||||
import io.legado.app.utils.StringUtils.formatFriendlyDate
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
@@ -226,7 +227,7 @@ fun ReadRecordScreen(
|
||||
modifier = Modifier.adaptiveHorizontalPadding(),
|
||||
visible = showSearch
|
||||
) {
|
||||
SearchBarSection(
|
||||
SearchBar(
|
||||
query = state.searchKey ?: "",
|
||||
onQueryChange = { viewModel.setSearchKey(it) }
|
||||
)
|
||||
@@ -1044,13 +1045,5 @@ fun BookStackView(coverPaths: List<String?>) {
|
||||
}
|
||||
|
||||
fun formatDuring(mss: Long): String {
|
||||
val days = mss / (1000 * 60 * 60 * 24)
|
||||
val hours = mss % (1000 * 60 * 60 * 24) / (1000 * 60 * 60)
|
||||
val minutes = mss % (1000 * 60 * 60) / (1000 * 60)
|
||||
val seconds = mss % (1000 * 60) / 1000
|
||||
val d = if (days > 0) "${days}天" else ""
|
||||
val h = if (hours > 0) "${hours}小时" else ""
|
||||
val m = if (minutes > 0) "${minutes}分钟" else ""
|
||||
val s = if (seconds > 0) "${seconds}秒" else ""
|
||||
return if ("$d$h$m$s".isBlank()) "0秒" else "$d$h$m$s"
|
||||
return formatReadDuration(mss)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.legado.app.ui.book.search
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
@@ -68,15 +70,15 @@ import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import io.legado.app.ui.widget.components.AppSearchBar
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.ui.widget.components.book.SearchBookListItem
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallTextButton
|
||||
import io.legado.app.ui.widget.components.card.NormalCard
|
||||
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.button.ToggleChip
|
||||
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.button.TopBarAnimatedActionButton
|
||||
@@ -84,6 +86,7 @@ import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
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.list.TopFloatingStickyItem
|
||||
import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.utils.toastOnUi
|
||||
@@ -104,8 +107,8 @@ fun SearchScreen(
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val listState = rememberLazyListState()
|
||||
var queryInput by rememberSaveable { mutableStateOf(state.query) }
|
||||
var searchExpanded by rememberSaveable { mutableStateOf(false) }
|
||||
var scopeSheetTab by rememberSaveable { mutableStateOf(0) }
|
||||
val showSuggestionPanel = state.showSuggestions
|
||||
val latestQuery by rememberUpdatedState(state.query)
|
||||
val scrollBehavior = if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
||||
GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
@@ -149,15 +152,13 @@ fun SearchScreen(
|
||||
state.hasMore,
|
||||
state.isManualStop,
|
||||
state.showSuggestions,
|
||||
searchExpanded,
|
||||
) {
|
||||
if (
|
||||
shouldLoadMore &&
|
||||
!state.isSearching &&
|
||||
state.hasMore &&
|
||||
!state.isManualStop &&
|
||||
!state.showSuggestions &&
|
||||
!searchExpanded
|
||||
!state.showSuggestions
|
||||
) {
|
||||
viewModel.onIntent(SearchIntent.LoadMore)
|
||||
}
|
||||
@@ -203,104 +204,60 @@ fun SearchScreen(
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
if (!searchExpanded) {
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.SetScopeSheetVisible(true))
|
||||
},
|
||||
imageVector = AppIcons.Filter
|
||||
)
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.OpenSourceManage)
|
||||
},
|
||||
imageVector = AppIcons.Settings,
|
||||
)
|
||||
}
|
||||
TopBarAnimatedActionButton(
|
||||
checked = state.isPrecisionSearch,
|
||||
onCheckedChange = { checked ->
|
||||
viewModel.onIntent(SearchIntent.TogglePrecision(checked))
|
||||
},
|
||||
iconChecked = AppIcons.PrecisionSearch,
|
||||
iconUnchecked = AppIcons.UnPrecisionSearch,
|
||||
activeText = stringResource(R.string.precision_search),
|
||||
inactiveText = stringResource(R.string.search),
|
||||
)
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.SetScopeSheetVisible(true))
|
||||
},
|
||||
imageVector = AppIcons.Filter
|
||||
)
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.OpenSourceManage)
|
||||
},
|
||||
imageVector = AppIcons.Settings,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AppSearchBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
query = queryInput,
|
||||
onQueryChange = { queryInput = it },
|
||||
onSearch = submitSearch,
|
||||
expanded = searchExpanded,
|
||||
onExpandedChange = { searchExpanded = it },
|
||||
label = searchLabel,
|
||||
placeholder = { Text(text = searchLabel) },
|
||||
leadingIcon = {
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
if (searchExpanded) {
|
||||
searchExpanded = false
|
||||
}
|
||||
},
|
||||
imageVector = if (searchExpanded) AppIcons.Back else AppIcons.Search,
|
||||
contentDescription = if (searchExpanded) stringResource(R.string.search) else stringResource(
|
||||
R.string.search
|
||||
)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (queryInput.isNotEmpty()) {
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
queryInput = ""
|
||||
viewModel.onIntent(SearchIntent.UpdateQuery(""))
|
||||
},
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.clear)
|
||||
)
|
||||
}
|
||||
TopBarAnimatedActionButton(
|
||||
checked = state.isPrecisionSearch,
|
||||
onCheckedChange = { checked ->
|
||||
viewModel.onIntent(SearchIntent.TogglePrecision(checked))
|
||||
},
|
||||
iconChecked = AppIcons.PrecisionSearch,
|
||||
iconUnchecked = AppIcons.UnPrecisionSearch,
|
||||
activeText = stringResource(R.string.precision_search),
|
||||
inactiveText = stringResource(R.string.search),
|
||||
)
|
||||
if (searchExpanded) {
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.SetScopeSheetVisible(true))
|
||||
},
|
||||
imageVector = AppIcons.Filter,
|
||||
contentDescription = stringResource(R.string.search_select_group)
|
||||
)
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.OpenSourceManage)
|
||||
},
|
||||
imageVector = AppIcons.Settings,
|
||||
contentDescription = stringResource(R.string.book_source_manage)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.adaptiveHorizontalPadding()
|
||||
) {
|
||||
SearchSuggestionPanel(
|
||||
state = state,
|
||||
onUseHistory = { keyword ->
|
||||
queryInput = keyword
|
||||
viewModel.onIntent(SearchIntent.UseHistoryKeyword(keyword))
|
||||
searchExpanded = false
|
||||
SearchBar(
|
||||
query = queryInput,
|
||||
onQueryChange = { queryInput = it },
|
||||
onSearch = submitSearch,
|
||||
placeholder = searchLabel,
|
||||
trailingIcon = {
|
||||
Row(
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (queryInput.isNotEmpty()) {
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
queryInput = ""
|
||||
viewModel.onIntent(SearchIntent.UpdateQuery(""))
|
||||
},
|
||||
imageVector = AppIcons.Close,
|
||||
contentDescription = stringResource(R.string.clear)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onDeleteHistory = { viewModel.onIntent(SearchIntent.DeleteHistory(it)) },
|
||||
onOpenBook = {
|
||||
viewModel.onIntent(SearchIntent.OpenBookshelfBook(it))
|
||||
searchExpanded = false
|
||||
},
|
||||
onClearHistory = {
|
||||
viewModel.onIntent(SearchIntent.SetClearHistoryDialogVisible(true))
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -330,73 +287,113 @@ fun SearchScreen(
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
if (showResultCountCard || showSourceProgressCard) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 36.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
if (showResultCountCard) {
|
||||
TextCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
icon = AppIcons.Search,
|
||||
text = "搜索结果 ${state.results.size}"
|
||||
)
|
||||
}
|
||||
if (showSourceProgressCard) {
|
||||
TextCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
icon = AppIcons.Filter,
|
||||
text = "已检索源 ${state.processedSources}/${state.totalSources}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (state.results.isEmpty()) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
SearchResultFooter(
|
||||
isSearching = state.isSearching,
|
||||
hasMore = state.hasMore,
|
||||
hasResult = false,
|
||||
committedQuery = state.committedQuery,
|
||||
onLoadMore = { viewModel.onIntent(SearchIntent.LoadMore) },
|
||||
AnimatedContent(
|
||||
targetState = showSuggestionPanel,
|
||||
label = "SearchBodyTransition",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { isSuggestionVisible ->
|
||||
if (isSuggestionVisible) {
|
||||
SearchSuggestionPanel(
|
||||
state = state,
|
||||
onUseHistory = { keyword ->
|
||||
queryInput = keyword
|
||||
viewModel.onIntent(SearchIntent.UseHistoryKeyword(keyword))
|
||||
},
|
||||
onDeleteHistory = { viewModel.onIntent(SearchIntent.DeleteHistory(it)) },
|
||||
onOpenBook = {
|
||||
viewModel.onIntent(SearchIntent.OpenBookshelfBook(it))
|
||||
},
|
||||
onClearHistory = {
|
||||
viewModel.onIntent(SearchIntent.SetClearHistoryDialogVisible(true))
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
items(items = state.results, key = { it.bookUrl }) { book ->
|
||||
val shelfStateFlow = remember(book.bookUrl) {
|
||||
viewModel.getBookShelfStateFlow(book)
|
||||
}
|
||||
SearchBookListItem(
|
||||
book = book,
|
||||
shelfState = shelfStateFlow,
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.OpenSearchBook(book))
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
item {
|
||||
SearchResultFooter(
|
||||
isSearching = state.isSearching,
|
||||
hasMore = state.hasMore,
|
||||
hasResult = true,
|
||||
committedQuery = state.committedQuery,
|
||||
onLoadMore = { viewModel.onIntent(SearchIntent.LoadMore) },
|
||||
)
|
||||
if (state.results.isEmpty()) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
SearchResultFooter(
|
||||
isSearching = state.isSearching,
|
||||
hasMore = state.hasMore,
|
||||
hasResult = false,
|
||||
committedQuery = state.committedQuery,
|
||||
onLoadMore = { viewModel.onIntent(SearchIntent.LoadMore) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.results.isNotEmpty()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
contentPadding = adaptiveContentPaddingOnlyVertical(
|
||||
top = 48.dp,
|
||||
bottom = 8.dp
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
items(items = state.results, key = { it.bookUrl }) { book ->
|
||||
val shelfStateFlow = remember(book.bookUrl) {
|
||||
viewModel.getBookShelfStateFlow(book)
|
||||
}
|
||||
SearchBookListItem(
|
||||
book = book,
|
||||
shelfState = shelfStateFlow,
|
||||
onClick = {
|
||||
viewModel.onIntent(SearchIntent.OpenSearchBook(book))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SearchResultFooter(
|
||||
isSearching = state.isSearching,
|
||||
hasMore = state.hasMore,
|
||||
hasResult = true,
|
||||
committedQuery = state.committedQuery,
|
||||
onLoadMore = { viewModel.onIntent(SearchIntent.LoadMore) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TopFloatingStickyItem(
|
||||
item = if (showResultCountCard || showSourceProgressCard) {
|
||||
SearchFloatingSummary(
|
||||
resultText = if (showResultCountCard) "结果 ${state.results.size}" else null,
|
||||
sourceText = if (showSourceProgressCard) " · 进度 ${state.processedSources}/${state.totalSources}" else null,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopCenter)
|
||||
.padding(top = 6.dp),
|
||||
) { summary ->
|
||||
NormalCard(
|
||||
cornerRadius = 16.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
contentColor = LegadoTheme.colorScheme.onCardContainer
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
) {
|
||||
summary.resultText?.let { text ->
|
||||
AppText(text = text, style = LegadoTheme.typography.labelSmallEmphasized)
|
||||
}
|
||||
summary.sourceText?.let { text ->
|
||||
AppText(text = text, style = LegadoTheme.typography.labelSmallEmphasized)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,6 +517,11 @@ fun SearchScreen(
|
||||
}
|
||||
}
|
||||
|
||||
private data class SearchFloatingSummary(
|
||||
val resultText: String?,
|
||||
val sourceText: String?,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun SearchSuggestionPanel(
|
||||
state: SearchUiState,
|
||||
@@ -565,7 +567,10 @@ private fun SearchSuggestionPanel(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AppIcon(AppIcons.History, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
AppText(
|
||||
@@ -636,9 +641,16 @@ private fun SearchResultFooter(
|
||||
) {
|
||||
when {
|
||||
isSearching -> {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
CircularProgressIndicator(modifier = Modifier.width(18.dp), strokeWidth = 2.dp)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(18.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
AppText(text = stringResource(R.string.is_loading))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.button.MediumOutlinedButton
|
||||
import io.legado.app.ui.widget.components.button.SmallAnimatedActionButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
@@ -167,7 +167,7 @@ fun SearchContentScreen(
|
||||
Box(
|
||||
modifier = Modifier.adaptiveHorizontalPadding()
|
||||
) {
|
||||
SearchBarSection(
|
||||
SearchBar(
|
||||
query = searchQuery,
|
||||
scrollState = listState,
|
||||
onQueryChange = { viewModel.onQueryChange(it) }
|
||||
|
||||
@@ -104,6 +104,7 @@ import io.legado.app.ui.widget.components.SelectionBottomBar
|
||||
import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet
|
||||
import io.legado.app.ui.widget.components.bookmark.BookmarkItem
|
||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
||||
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.divider.PillDivider
|
||||
import io.legado.app.ui.widget.components.divider.PillHeaderDivider
|
||||
@@ -950,13 +951,13 @@ private fun StatusIcon(
|
||||
}
|
||||
|
||||
"SUCCESS_WORD_COUNT" -> {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
NormalCard(
|
||||
cornerRadius = 12.dp,
|
||||
border = BorderStroke(
|
||||
1.dp,
|
||||
LegadoTheme.colorScheme.outlineVariant
|
||||
),
|
||||
color = Color.Transparent
|
||||
containerColor = Color.Transparent
|
||||
) {
|
||||
if (wordCount != null) {
|
||||
AppText(
|
||||
|
||||
@@ -36,7 +36,9 @@ import coil.compose.AsyncImage
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.card.NormalCard
|
||||
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.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
|
||||
@@ -87,9 +89,8 @@ fun CoverManageSheet(
|
||||
) {
|
||||
items(coverList) { path ->
|
||||
Box {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
tonalElevation = 2.dp
|
||||
NormalCard(
|
||||
cornerRadius = 12.dp
|
||||
) {
|
||||
AsyncImage(
|
||||
model = path,
|
||||
@@ -122,16 +123,16 @@ fun CoverManageSheet(
|
||||
}
|
||||
|
||||
item {
|
||||
Surface(
|
||||
NormalCard(
|
||||
onClick = { showFilePicker = true },
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainerHigh,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(0.7f)
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
AppIcon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = "Add",
|
||||
modifier = Modifier.size(32.dp),
|
||||
|
||||
@@ -32,6 +32,7 @@ import coil.compose.AsyncImage
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.card.NormalCard
|
||||
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.modalBottomSheet.AppModalBottomSheet
|
||||
@@ -80,10 +81,10 @@ fun BackgroundImageManageSheet(
|
||||
) {
|
||||
|
||||
if (currentPath.isNullOrBlank()) {
|
||||
Surface(
|
||||
NormalCard(
|
||||
onClick = { showFilePicker = true },
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
color = LegadoTheme.colorScheme.surfaceContainerHigh,
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainerHigh,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(16f / 9f)
|
||||
@@ -104,9 +105,8 @@ fun BackgroundImageManageSheet(
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
tonalElevation = 2.dp
|
||||
NormalCard(
|
||||
cornerRadius = 12.dp,
|
||||
) {
|
||||
AsyncImage(
|
||||
model = currentPath,
|
||||
|
||||
@@ -92,7 +92,7 @@ fun ThemeListDialog(
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
cornerRadius = 20.dp,
|
||||
containerColor = if (item.primaryColor.toColorInt() == context.primaryColor) {
|
||||
MaterialTheme.colorScheme.secondaryContainer
|
||||
} else {
|
||||
|
||||
@@ -315,7 +315,6 @@ fun MainScreen(
|
||||
AppNavigationBar(
|
||||
modifier = Modifier
|
||||
.regularHazeEffect(state = hazeState)
|
||||
.height(if (isUnlabeled) 64.dp else 80.dp)
|
||||
) {
|
||||
destinations.forEachIndexed { index, destination ->
|
||||
val selected = pagerState.targetPage == index
|
||||
|
||||
@@ -77,6 +77,7 @@ import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
||||
import io.legado.app.ui.widget.components.card.NormalCard
|
||||
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.lazylist.FastScrollLazyVerticalGrid
|
||||
@@ -568,9 +569,9 @@ fun BookshelfScreen(
|
||||
|
||||
if (uiState.isLoading) {
|
||||
Dialog(onDismissRequest = {}) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh
|
||||
NormalCard(
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainerHigh
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(24.dp),
|
||||
|
||||
@@ -345,7 +345,7 @@ fun ExploreSourceHeader(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = containerColor
|
||||
) {
|
||||
ListItem(
|
||||
|
||||
@@ -180,7 +180,8 @@ fun AppTheme(
|
||||
surfaceContainer = miuixColorScheme.surfaceContainer,
|
||||
surfaceContainerHigh = miuixColorScheme.surfaceContainerHigh,
|
||||
surfaceContainerHighest = miuixColorScheme.surfaceContainerHighest,
|
||||
surfaceContainerLow = miuixColorScheme.secondaryContainer,
|
||||
surfaceContainerLow = miuixColorScheme.secondaryContainer.copy(alpha = 0.32f)
|
||||
.compositeOver(miuixColorScheme.surface),
|
||||
surfaceContainerLowest = miuixColorScheme.background,
|
||||
|
||||
primaryFixed = miuixColorScheme.primaryContainer,
|
||||
|
||||
@@ -39,7 +39,7 @@ fun CollapsibleHeader(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.adaptiveHorizontalPadding(vertical = 4.dp),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
onClick = onToggle
|
||||
) {
|
||||
|
||||
+14
-2
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.input.TextFieldLineLimits
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -15,6 +16,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
@@ -26,10 +28,11 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SearchBarSection(
|
||||
fun SearchBar(
|
||||
modifier: Modifier = Modifier,
|
||||
query: String,
|
||||
onQueryChange: (String) -> Unit,
|
||||
onSearch: (String) -> Unit = {},
|
||||
placeholder: String = "搜索...",
|
||||
leadingIcon: @Composable (() -> Unit)? = {
|
||||
AppIcon(
|
||||
@@ -79,10 +82,15 @@ fun SearchBarSection(
|
||||
placeholder = { AppText(placeholder) },
|
||||
leadingIcon = leadingIcon,
|
||||
trailingIcon = trailingIcon,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
onKeyboardAction = {
|
||||
onSearch(textFieldState.text.toString())
|
||||
},
|
||||
lineLimits = TextFieldLineLimits.SingleLine,
|
||||
backgroundColor = resolvedBackgroundColor,
|
||||
miuixUseSearchBarInputField = true,
|
||||
miuixSearchBarLabel = placeholder
|
||||
miuixSearchBarLabel = placeholder,
|
||||
miuixOnSearch = onSearch,
|
||||
)
|
||||
} else {
|
||||
Surface(
|
||||
@@ -98,6 +106,10 @@ fun SearchBarSection(
|
||||
placeholder = { AppText(placeholder) },
|
||||
leadingIcon = leadingIcon,
|
||||
trailingIcon = trailingIcon,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
onKeyboardAction = {
|
||||
onSearch(textFieldState.text.toString())
|
||||
},
|
||||
lineLimits = TextFieldLineLimits.SingleLine,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
@@ -17,6 +17,8 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Shuffle
|
||||
import androidx.compose.material.icons.filled.SwapHoriz
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -59,7 +61,7 @@ fun SearchBookListItem(
|
||||
BookShelfState.SAME_NAME_AUTHOR -> {
|
||||
{
|
||||
androidx.compose.material3.Icon(
|
||||
imageVector = Icons.Default.Warning,
|
||||
imageVector = Icons.Default.Shuffle,
|
||||
contentDescription = "同名书籍",
|
||||
modifier = Modifier.size(12.dp),
|
||||
)
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package io.legado.app.ui.widget.components.card
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CardElevation
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
@@ -25,12 +28,12 @@ import top.yukonga.miuix.kmp.basic.CardDefaults as MiuixCardDefaults
|
||||
private fun BaseCard(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
shape: Shape = CardDefaults.shape,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
cornerRadius: Dp = MiuixCardDefaults.CornerRadius,
|
||||
pressFeedbackType: PressFeedbackType = PressFeedbackType.None,
|
||||
containerColor: Color? = null,
|
||||
contentColor: Color? = null,
|
||||
elevation: CardElevation = CardDefaults.cardElevation(),
|
||||
elevation: Dp = 0.dp,
|
||||
border: BorderStroke? = null,
|
||||
alpha: Float = 1f,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
@@ -47,6 +50,7 @@ private fun BaseCard(
|
||||
pressFeedbackType = pressFeedbackType,
|
||||
showIndication = true,
|
||||
onClick = onClick,
|
||||
onLongPress = onLongClick,
|
||||
content = content,
|
||||
colors = colors
|
||||
)
|
||||
@@ -67,25 +71,26 @@ private fun BaseCard(
|
||||
disabledContainerColor = LegadoTheme.colorScheme.onSecondaryContainer.copy(alpha = alpha * 0.38f),
|
||||
disabledContentColor = LegadoTheme.colorScheme.onSecondaryContainer.copy(alpha = alpha * 0.38f)
|
||||
)
|
||||
if (onClick != null) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(cornerRadius),
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
onClick = onClick,
|
||||
content = content
|
||||
)
|
||||
val clickableModifier = if (onClick != null || onLongClick != null) {
|
||||
modifier
|
||||
.clip(RoundedCornerShape(cornerRadius))
|
||||
.combinedClickable(
|
||||
onClick = { onClick?.invoke() },
|
||||
onLongClick = onLongClick
|
||||
)
|
||||
} else {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(cornerRadius),
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
content = content
|
||||
)
|
||||
modifier
|
||||
}
|
||||
Surface(
|
||||
modifier = clickableModifier,
|
||||
shape = RoundedCornerShape(cornerRadius),
|
||||
color = colors.containerColor,
|
||||
contentColor = colors.contentColor,
|
||||
tonalElevation = 0.dp,
|
||||
shadowElevation = elevation,
|
||||
border = border
|
||||
) {
|
||||
Column(content = content)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,19 +100,19 @@ private fun BaseCard(
|
||||
fun GlassCard(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
shape: Shape = CardDefaults.shape,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
cornerRadius: Dp = MiuixCardDefaults.CornerRadius,
|
||||
pressFeedbackType: PressFeedbackType = PressFeedbackType.None,
|
||||
containerColor: Color? = null,
|
||||
contentColor: Color? = null,
|
||||
elevation: CardElevation = CardDefaults.cardElevation(),
|
||||
elevation: Dp = 0.dp,
|
||||
border: BorderStroke? = null,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
BaseCard(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
shape = shape,
|
||||
onLongClick = onLongClick,
|
||||
cornerRadius = cornerRadius,
|
||||
pressFeedbackType = pressFeedbackType,
|
||||
containerColor = containerColor,
|
||||
@@ -124,19 +129,19 @@ fun GlassCard(
|
||||
fun NormalCard(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
shape: Shape = CardDefaults.shape,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
cornerRadius: Dp = MiuixCardDefaults.CornerRadius,
|
||||
pressFeedbackType: PressFeedbackType = PressFeedbackType.None,
|
||||
containerColor: Color? = null,
|
||||
contentColor: Color? = null,
|
||||
elevation: CardElevation = CardDefaults.cardElevation(),
|
||||
elevation: Dp = 0.dp,
|
||||
border: BorderStroke? = null,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
BaseCard(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
shape = shape,
|
||||
onLongClick = onLongClick,
|
||||
cornerRadius = cornerRadius,
|
||||
pressFeedbackType = pressFeedbackType,
|
||||
containerColor = containerColor,
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.foundation.lazy.LazyItemScope
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
@@ -88,9 +87,9 @@ fun SelectionItemCard(
|
||||
onClick = onToggleSelection,
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = animatedContainerColor,
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = elevation)
|
||||
elevation = elevation
|
||||
) {
|
||||
SelectionItemCardContent(
|
||||
title = title,
|
||||
|
||||
@@ -36,12 +36,12 @@ fun ExploreKindItem(
|
||||
LocalMinimumInteractiveComponentSize provides Dp.Unspecified
|
||||
) {
|
||||
|
||||
val shape = MaterialTheme.shapes.medium
|
||||
val cornerRadius = 12.dp
|
||||
|
||||
if (isClickable) {
|
||||
GlassCard(
|
||||
onClick = onClick,
|
||||
shape = shape,
|
||||
cornerRadius = cornerRadius,
|
||||
containerColor = backgroundColor,
|
||||
contentColor = LegadoTheme.colorScheme.onSurface,
|
||||
modifier = modifier
|
||||
@@ -54,7 +54,7 @@ fun ExploreKindItem(
|
||||
}
|
||||
} else {
|
||||
GlassCard(
|
||||
shape = shape,
|
||||
cornerRadius = cornerRadius,
|
||||
containerColor = backgroundColor,
|
||||
contentColor = LegadoTheme.colorScheme.primary,
|
||||
modifier = modifier,
|
||||
|
||||
+5
-4
@@ -18,6 +18,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.card.NormalCard
|
||||
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
|
||||
@@ -58,14 +59,14 @@ fun RowScope.OptionCard(
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Surface(
|
||||
NormalCard(
|
||||
onClick = onClick,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(100.dp),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
color = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||
tonalElevation = 2.dp
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||
elevation = 2.dp
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
||||
@@ -21,7 +21,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
@@ -107,7 +107,7 @@ fun <T> DynamicTopAppBar(
|
||||
enter = expandVertically() + fadeIn(),
|
||||
exit = shrinkVertically() + fadeOut()
|
||||
) {
|
||||
SearchBarSection(
|
||||
SearchBar(
|
||||
query = state.searchKey,
|
||||
onQueryChange = onSearchQueryChange,
|
||||
placeholder = searchPlaceholder,
|
||||
|
||||
@@ -246,6 +246,7 @@
|
||||
<string name="threads_num_title">更新和搜索线程数</string>
|
||||
<string name="change_icon">切换图标</string>
|
||||
<string name="remove_from_bookshelf">已在书架</string>
|
||||
<string name="already_in_bookshelf">已在书架</string>
|
||||
<string name="start_read">开始阅读</string>
|
||||
<string name="data_loading">加载数据中…</string>
|
||||
<string name="load_error_retry">加载失败</string>
|
||||
@@ -395,6 +396,7 @@
|
||||
<string name="imm_navigation_bar_s">导航栏颜色透明</string>
|
||||
<string name="add_to_bookshelf">放入书架</string>
|
||||
<string name="click_to_remove">点击删除</string>
|
||||
<string name="long_press_group">长按分组</string>
|
||||
<string name="continue_read">继续阅读</string>
|
||||
<string name="cover_path">封面地址</string>
|
||||
<string name="page_anim_cover">覆盖</string>
|
||||
|
||||
@@ -223,6 +223,7 @@
|
||||
<string name="threads_num_title">更新/搜尋線程數,太多會卡頓</string>
|
||||
<string name="change_icon">切換圖標</string>
|
||||
<string name="remove_from_bookshelf">刪除書輯</string>
|
||||
<string name="already_in_bookshelf">已在書架</string>
|
||||
<string name="start_read">開始閲讀</string>
|
||||
<string name="data_loading">數據載入中…</string>
|
||||
<string name="load_error_retry">載入失敗,點擊重試</string>
|
||||
|
||||
@@ -225,6 +225,7 @@
|
||||
<string name="threads_num_title">更新和搜尋執行緒數,太多會卡頓</string>
|
||||
<string name="change_icon">切換圖示</string>
|
||||
<string name="remove_from_bookshelf">刪除書籍</string>
|
||||
<string name="already_in_bookshelf">已在書架</string>
|
||||
<string name="start_read">開始閱讀</string>
|
||||
<string name="data_loading">載入資料中…</string>
|
||||
<string name="load_error_retry">載入失敗,點擊重試</string>
|
||||
@@ -373,6 +374,7 @@
|
||||
<string name="imm_navigation_bar_s">導航欄顏色透明</string>
|
||||
<string name="add_to_bookshelf">放入書架</string>
|
||||
<string name="click_to_remove">點擊刪除</string>
|
||||
<string name="long_press_group">長按分組</string>
|
||||
<string name="continue_read">繼續閱讀</string>
|
||||
<string name="cover_path">封面地址</string>
|
||||
<string name="page_anim_cover">覆蓋</string>
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
<string name="threads_num_title">Number of concurrent tasks</string>
|
||||
<string name="change_icon">Change icon</string>
|
||||
<string name="remove_from_bookshelf">In library</string>
|
||||
<string name="already_in_bookshelf">Already in Bookshelf</string>
|
||||
<string name="start_read">Start reading</string>
|
||||
<string name="data_loading">Loading…</string>
|
||||
<string name="load_error_retry">Load failed, tap to retry</string>
|
||||
@@ -398,6 +399,7 @@
|
||||
<string name="imm_navigation_bar_s">The navigation bar becomes transparent</string>
|
||||
<string name="add_to_bookshelf">Add to Bookshelf</string>
|
||||
<string name="click_to_remove">Tap to remove</string>
|
||||
<string name="long_press_group">Long press to group</string>
|
||||
<string name="continue_read">Continue reading</string>
|
||||
<string name="cover_path">Cover path</string>
|
||||
<string name="page_anim_cover">Cover</string>
|
||||
|
||||
Reference in New Issue
Block a user