一些神必优化
This commit is contained in:
@@ -4,10 +4,13 @@ import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
|
||||
val Context.localDataStore: DataStore<Preferences> by preferencesDataStore(name = "local_ui_status")
|
||||
|
||||
object LocalPreferencesKeys {
|
||||
val SHOW_THEME_REFACTOR_TIP = booleanPreferencesKey("show_theme_refactor_tip")
|
||||
val SEARCH_LAYOUT_MODE = intPreferencesKey("search_layout_mode")
|
||||
val MATCH_MODE = intPreferencesKey("match_mode")
|
||||
}
|
||||
|
||||
@@ -80,9 +80,10 @@ class ExploreRepositoryImpl(
|
||||
override suspend fun exploreBooks(
|
||||
bookSource: BookSource,
|
||||
url: String,
|
||||
page: Int
|
||||
page: Int,
|
||||
key: String?
|
||||
): List<SearchBook> {
|
||||
return WebBook.exploreBookSuspend(bookSource, url, page)
|
||||
return WebBook.exploreBookSuspend(bookSource, url, page, key = key, isSearch = key != null)
|
||||
}
|
||||
|
||||
override suspend fun getSourceExploreKinds(sourceUrl: String): List<ExploreKind> = withContext(IO) {
|
||||
|
||||
@@ -5,5 +5,10 @@ import io.legado.app.data.entities.SearchBook
|
||||
|
||||
interface ExploreBooksGateway {
|
||||
suspend fun getBookSource(sourceUrl: String): BookSource?
|
||||
suspend fun exploreBooks(bookSource: BookSource, url: String, page: Int): List<SearchBook>
|
||||
suspend fun exploreBooks(
|
||||
bookSource: BookSource,
|
||||
url: String,
|
||||
page: Int,
|
||||
key: String? = null
|
||||
): List<SearchBook>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.legado.app.domain.model
|
||||
|
||||
enum class MatchMode(val value: Int) {
|
||||
DEFAULT(0),
|
||||
EXACT(1);
|
||||
|
||||
companion object {
|
||||
fun of(value: Int) = entries.getOrElse(value) { DEFAULT }
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,17 @@ class ExploreBooksUseCase(
|
||||
sourceUrl: String,
|
||||
moduleUrl: String?,
|
||||
args: String?,
|
||||
page: Int = 1
|
||||
page: Int = 1,
|
||||
key: String? = null,
|
||||
): ExploreResult = withContext(Dispatchers.IO) {
|
||||
val base = gateway.getBookSource(sourceUrl)
|
||||
?: throw SourceNotFound(sourceUrl)
|
||||
val source = args?.let { base.copy().also { s -> s.setVariable(it) } } ?: base
|
||||
val resolvedUrl = moduleUrl ?: source.exploreUrl
|
||||
?: throw NoExploreUrl(sourceUrl)
|
||||
val books = gateway.exploreBooks(source, resolvedUrl, page)
|
||||
val resolvedUrl = moduleUrl
|
||||
?: (if (key != null) source.searchUrl else null)
|
||||
?: source.exploreUrl
|
||||
?: throw NoExploreUrl(sourceUrl)
|
||||
val books = gateway.exploreBooks(source, resolvedUrl, page, key = key)
|
||||
ExploreResult(resolvedUrl, books)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,9 +109,12 @@ object WebBook {
|
||||
url: String,
|
||||
page: Int? = 1,
|
||||
ruleData: RuleDataInterface = RuleData(),
|
||||
key: String? = null,
|
||||
isSearch: Boolean = false,
|
||||
): ArrayList<SearchBook> {
|
||||
val analyzeUrl = AnalyzeUrl(
|
||||
mUrl = url,
|
||||
key = key,
|
||||
page = page,
|
||||
baseUrl = bookSource.bookSourceUrl,
|
||||
source = bookSource,
|
||||
@@ -137,7 +140,7 @@ object WebBook {
|
||||
analyzeUrl = analyzeUrl,
|
||||
baseUrl = res.url,
|
||||
body = res.body,
|
||||
isSearch = false
|
||||
isSearch = isSearch
|
||||
)
|
||||
}
|
||||
|
||||
@@ -146,8 +149,10 @@ object WebBook {
|
||||
url: String,
|
||||
page: Int? = 1,
|
||||
ruleData: RuleDataInterface = RuleData(),
|
||||
key: String? = null,
|
||||
isSearch: Boolean = false,
|
||||
): List<SearchBook> {
|
||||
return exploreBookAwait(bookSource, url, page, ruleData)
|
||||
return exploreBookAwait(bookSource, url, page, ruleData, key, isSearch)
|
||||
}
|
||||
|
||||
suspend fun exploreBookWithResolvedUrl(
|
||||
|
||||
+11
-11
@@ -40,7 +40,7 @@ import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
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.icon.AppIcon
|
||||
@@ -347,7 +347,7 @@ private fun BookCacheBookCard(
|
||||
color = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
if (item.hasDownloadTask || item.cachedCount < item.totalCount) {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = {
|
||||
if (item.hasActiveDownload) {
|
||||
onIntent(BookCacheManageIntent.StopBookDownload(item.bookUrl))
|
||||
@@ -355,7 +355,7 @@ private fun BookCacheBookCard(
|
||||
onIntent(BookCacheManageIntent.StartBookDownload(item.bookUrl))
|
||||
}
|
||||
},
|
||||
imageVector = if (item.hasActiveDownload) Icons.Default.Stop else Icons.Default.PlayArrow,
|
||||
icon = if (item.hasActiveDownload) Icons.Default.Stop else Icons.Default.PlayArrow,
|
||||
contentDescription = when {
|
||||
item.hasActiveDownload -> "暂停本书下载"
|
||||
item.isPaused -> "继续本书下载"
|
||||
@@ -363,9 +363,9 @@ private fun BookCacheBookCard(
|
||||
}
|
||||
)
|
||||
}
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = { onDeleteBook(item) },
|
||||
imageVector = Icons.Default.Delete,
|
||||
icon = Icons.Default.Delete,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
@@ -412,21 +412,21 @@ private fun BookCacheChapterRow(
|
||||
)
|
||||
}
|
||||
if (item.isWaiting || item.isDownloading) {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onStop,
|
||||
imageVector = Icons.Default.Stop,
|
||||
icon = Icons.Default.Stop,
|
||||
contentDescription = "暂停章节下载"
|
||||
)
|
||||
} else if (item.isPaused || !item.isCached) {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onDownload,
|
||||
imageVector = Icons.Default.Download,
|
||||
icon = Icons.Default.Download,
|
||||
contentDescription = if (item.isPaused) "继续章节下载" else "下载章节"
|
||||
)
|
||||
}
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onDelete,
|
||||
imageVector = Icons.Default.Delete,
|
||||
icon = Icons.Default.Delete,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.legado.app.ui.book.explore
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.data.entities.rule.ExploreKind
|
||||
import io.legado.app.domain.model.BookShelfState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Stable
|
||||
data class ExploreShowUiState(
|
||||
val sourceUrl: String? = null,
|
||||
val books: ImmutableList<ExploreBookItemUi> = persistentListOf(),
|
||||
val kinds: ImmutableList<ExploreKind> = persistentListOf(),
|
||||
val selectedKindTitle: String? = null,
|
||||
val layoutState: Int = 0,
|
||||
val gridCount: Int = 3,
|
||||
val isLoading: Boolean = false,
|
||||
val isRefreshing: Boolean = false,
|
||||
val isEnd: Boolean = false,
|
||||
val errorMsg: String? = null,
|
||||
val sheet: ExploreShowSheet = ExploreShowSheet.None,
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class ExploreBookItemUi(
|
||||
val book: SearchBook,
|
||||
val shelfState: BookShelfState = BookShelfState.NOT_IN_SHELF,
|
||||
)
|
||||
|
||||
sealed interface ExploreShowSheet {
|
||||
data object None : ExploreShowSheet
|
||||
data object KindSelect : ExploreShowSheet
|
||||
data object GridCount : ExploreShowSheet
|
||||
}
|
||||
|
||||
sealed interface ExploreShowIntent {
|
||||
data class InitData(
|
||||
val sourceUrl: String?,
|
||||
val exploreUrl: String?,
|
||||
) : ExploreShowIntent
|
||||
|
||||
data object LoadMore : ExploreShowIntent
|
||||
data object Refresh : ExploreShowIntent
|
||||
data class SwitchKind(val kind: ExploreKind) : ExploreShowIntent
|
||||
data object ToggleLayout : ExploreShowIntent
|
||||
data class SaveGridCount(val count: Int) : ExploreShowIntent
|
||||
data class ShowSheet(val sheet: ExploreShowSheet) : ExploreShowIntent
|
||||
data object DismissSheet : ExploreShowIntent
|
||||
data class OpenBook(val book: SearchBook, val sharedCoverKey: String?) : ExploreShowIntent
|
||||
data class AddToShelf(val book: SearchBook) : ExploreShowIntent
|
||||
}
|
||||
|
||||
sealed interface ExploreShowEffect {
|
||||
data class OpenBookInfo(
|
||||
val name: String,
|
||||
val author: String,
|
||||
val bookUrl: String,
|
||||
val origin: String?,
|
||||
val coverPath: String?,
|
||||
val sharedCoverKey: String?,
|
||||
) : ExploreShowEffect
|
||||
|
||||
data class ShowMessage(val message: String) : ExploreShowEffect
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import androidx.compose.animation.AnimatedVisibilityScope
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.SharedTransitionScope
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
@@ -33,11 +32,8 @@ import androidx.compose.material.icons.filled.GridView
|
||||
import androidx.compose.material.icons.outlined.FilterAlt
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -48,19 +44,19 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.domain.model.BookShelfState
|
||||
import io.legado.app.domain.usecase.ExploreKindUiUseCase
|
||||
import io.legado.app.ui.config.coverConfig.CoverConfig
|
||||
import io.legado.app.ui.main.bookCoverSharedElementKey
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
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.AppPullToRefresh
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.AppSlider
|
||||
import io.legado.app.ui.widget.components.LoadMoreFooter
|
||||
import io.legado.app.ui.widget.components.book.SearchBookGridItem
|
||||
import io.legado.app.ui.widget.components.book.SearchBookListItem
|
||||
@@ -74,7 +70,17 @@ import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
private enum class BookFilterState(val id: Int) {
|
||||
SHOW_ALL(0),
|
||||
HIDE_IN_SHELF(1),
|
||||
HIDE_SAME_NAME_AUTHOR(2),
|
||||
SHOW_NOT_IN_SHELF_ONLY(3);
|
||||
|
||||
companion object {
|
||||
fun fromId(id: Int) = entries.getOrElse(id) { SHOW_ALL }
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("LocalContextConfigurationRead", "ConfigurationScreenWidthHeight")
|
||||
@OptIn(
|
||||
@@ -83,55 +89,51 @@ import org.koin.compose.koinInject
|
||||
)
|
||||
@Composable
|
||||
fun ExploreShowScreen(
|
||||
title: String,
|
||||
sourceUrl: String?,
|
||||
exploreUrl: String?,
|
||||
viewModel: ExploreShowViewModel = koinViewModel(),
|
||||
title: String = "",
|
||||
onBack: () -> Unit,
|
||||
onBookClick: (SearchBook, String?) -> Unit,
|
||||
viewModel: ExploreShowViewModel = koinViewModel(),
|
||||
sharedTransitionScope: SharedTransitionScope? = null,
|
||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||
) {
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(viewModel) {
|
||||
viewModel.effects.collect { effect ->
|
||||
when (effect) {
|
||||
is ExploreShowEffect.OpenBookInfo -> onBookClick(
|
||||
SearchBook(
|
||||
name = effect.name,
|
||||
author = effect.author,
|
||||
bookUrl = effect.bookUrl,
|
||||
origin = effect.origin ?: "",
|
||||
coverUrl = effect.coverPath,
|
||||
),
|
||||
effect.sharedCoverKey,
|
||||
)
|
||||
|
||||
is ExploreShowEffect.ShowMessage -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var previewBook by remember { mutableStateOf<SearchBook?>(null) }
|
||||
var previewSharedCoverKey by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
LaunchedEffect(sourceUrl, exploreUrl, viewModel) {
|
||||
viewModel.initData(sourceUrl, exploreUrl)
|
||||
}
|
||||
|
||||
val rawBooks by viewModel.uiBooks.collectAsState()
|
||||
val filterStateId = CoverConfig.exploreFilterState
|
||||
val books = remember(rawBooks, filterStateId) {
|
||||
val books = remember(state.books, filterStateId) {
|
||||
val filter = BookFilterState.fromId(filterStateId)
|
||||
when (filter) {
|
||||
BookFilterState.SHOW_ALL -> rawBooks
|
||||
BookFilterState.HIDE_IN_SHELF -> rawBooks.filter { it.shelfState != BookShelfState.IN_SHELF }
|
||||
BookFilterState.HIDE_SAME_NAME_AUTHOR -> rawBooks.filter { it.shelfState != BookShelfState.SAME_NAME_AUTHOR }
|
||||
BookFilterState.SHOW_NOT_IN_SHELF_ONLY -> rawBooks.filter { it.shelfState == BookShelfState.NOT_IN_SHELF }
|
||||
BookFilterState.SHOW_ALL -> state.books
|
||||
BookFilterState.HIDE_IN_SHELF -> state.books.filter { it.shelfState != BookShelfState.IN_SHELF }
|
||||
BookFilterState.HIDE_SAME_NAME_AUTHOR -> state.books.filter { it.shelfState != BookShelfState.SAME_NAME_AUTHOR }
|
||||
BookFilterState.SHOW_NOT_IN_SHELF_ONLY -> state.books.filter { it.shelfState == BookShelfState.NOT_IN_SHELF }
|
||||
}
|
||||
}
|
||||
val isBookEnd by viewModel.isEnd.collectAsState()
|
||||
val shouldTriggerAutoLoad by viewModel.shouldTriggerAutoLoad.collectAsState()
|
||||
val isLoading by viewModel.isLoading.collectAsState()
|
||||
val errorMsg by viewModel.errorMsg.collectAsState()
|
||||
val selectedTitle by viewModel.selectedKindTitle.collectAsState()
|
||||
val listState = rememberLazyListState()
|
||||
val gridState = rememberLazyGridState()
|
||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||
var showKindSheet by remember { mutableStateOf(false) }
|
||||
val layoutState by viewModel.layoutState.collectAsState()
|
||||
val isGridMode = layoutState == 1
|
||||
var showGridCountSheet by remember { mutableStateOf(false) }
|
||||
val gridColumnCount by viewModel.gridCount.collectAsState()
|
||||
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||
val exploreKindUseCase: ExploreKindUiUseCase = koinInject()
|
||||
|
||||
LaunchedEffect(sourceUrl) {
|
||||
exploreKindUseCase.warmUp(sourceUrl)
|
||||
}
|
||||
|
||||
val isRefreshing by viewModel.isRefreshing.collectAsState()
|
||||
|
||||
val isGridMode = state.layoutState == 1
|
||||
val hazeState = remember { HazeState() }
|
||||
val shouldLoadMoreList = remember {
|
||||
derivedStateOf {
|
||||
@@ -150,17 +152,11 @@ fun ExploreShowScreen(
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldLoadMoreList.value, isGridMode) {
|
||||
if (!isGridMode && shouldLoadMoreList.value) viewModel.loadMore()
|
||||
if (!isGridMode && shouldLoadMoreList.value) viewModel.onIntent(ExploreShowIntent.LoadMore)
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldLoadMoreGrid.value, isGridMode) {
|
||||
if (isGridMode && shouldLoadMoreGrid.value) viewModel.loadMore()
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldTriggerAutoLoad) {
|
||||
if (shouldTriggerAutoLoad) {
|
||||
viewModel.loadMore()
|
||||
}
|
||||
if (isGridMode && shouldLoadMoreGrid.value) viewModel.onIntent(ExploreShowIntent.LoadMore)
|
||||
}
|
||||
|
||||
LaunchedEffect(isGridMode) {
|
||||
@@ -175,17 +171,14 @@ fun ExploreShowScreen(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AppModalBottomSheet(
|
||||
show = showGridCountSheet,
|
||||
modifier = Modifier
|
||||
.padding(16.dp),
|
||||
onDismissRequest = { showGridCountSheet = false }
|
||||
show = state.sheet == ExploreShowSheet.GridCount,
|
||||
onDismissRequest = { viewModel.onIntent(ExploreShowIntent.DismissSheet) }
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp),
|
||||
.padding(bottom = 32.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
@@ -193,49 +186,38 @@ fun ExploreShowScreen(
|
||||
text = "布局列数",
|
||||
style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
TextCard(
|
||||
text = "$gridColumnCount 列",
|
||||
text = "${state.gridCount} 列",
|
||||
textStyle = LegadoTheme.typography.titleSmall,
|
||||
backgroundColor = LegadoTheme.colorScheme.onSheetContent,
|
||||
verticalPadding = 4.dp,
|
||||
horizontalPadding = 12.dp,
|
||||
cornerRadius = 12.dp
|
||||
)
|
||||
}
|
||||
|
||||
Slider(
|
||||
value = gridColumnCount.toFloat(),
|
||||
AppSlider(
|
||||
value = state.gridCount.toFloat(),
|
||||
onValueChange = {
|
||||
val col = it.toInt().coerceIn(1, 10)
|
||||
viewModel.saveGridCount(col)
|
||||
viewModel.onIntent(ExploreShowIntent.SaveGridCount(it.toInt().coerceIn(1, 10)))
|
||||
},
|
||||
valueRange = 1f..10f,
|
||||
steps = 8,
|
||||
modifier = Modifier.padding(horizontal = 20.dp)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
OutlinedButton(
|
||||
onClick = { showGridCountSheet = false },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
AppText("完成")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ExploreKindSelectSheet(
|
||||
show = showKindSheet,
|
||||
onDismissRequest = { showKindSheet = false },
|
||||
sourceUrl = sourceUrl,
|
||||
show = state.sheet == ExploreShowSheet.KindSelect,
|
||||
onDismissRequest = { viewModel.onIntent(ExploreShowIntent.DismissSheet) },
|
||||
sourceUrl = state.sourceUrl,
|
||||
onSelected = { selectedKinds ->
|
||||
selectedKinds.firstOrNull()?.let { kind ->
|
||||
viewModel.switchExploreUrl(kind)
|
||||
viewModel.onIntent(ExploreShowIntent.SwitchKind(kind))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -245,39 +227,39 @@ fun ExploreShowScreen(
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
modifier = Modifier.responsiveHazeEffect(
|
||||
state = hazeState
|
||||
),
|
||||
title = selectedTitle ?: title,
|
||||
modifier = Modifier.responsiveHazeEffect(state = hazeState),
|
||||
title = state.selectedKindTitle ?: title,
|
||||
navigationIcon = {
|
||||
TopBarNavigationButton(onClick = onBack)
|
||||
},
|
||||
actions = {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier.animateContentSize(tween(300))
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isGridMode,
|
||||
enter = fadeIn(tween(300)),
|
||||
exit = fadeOut(tween(300))
|
||||
) {
|
||||
TopBarActionButton(
|
||||
onClick = { showKindSheet = true },
|
||||
imageVector = Icons.Outlined.FilterAlt,
|
||||
contentDescription = "分类"
|
||||
onClick = {
|
||||
viewModel.onIntent(
|
||||
ExploreShowIntent.ShowSheet(
|
||||
ExploreShowSheet.GridCount
|
||||
)
|
||||
)
|
||||
},
|
||||
imageVector = Icons.AutoMirrored.Outlined.FormatListBulleted,
|
||||
contentDescription = "列数设置"
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isGridMode,
|
||||
enter = fadeIn(tween(300)),
|
||||
exit = fadeOut(tween(300))
|
||||
) {
|
||||
TopBarActionButton(
|
||||
onClick = { showGridCountSheet = true },
|
||||
imageVector = Icons.AutoMirrored.Outlined.FormatListBulleted,
|
||||
contentDescription = "列数设置"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TopBarActionButton(
|
||||
onClick = { viewModel.setLayout() },
|
||||
onClick = { viewModel.onIntent(ExploreShowIntent.ShowSheet(ExploreShowSheet.KindSelect)) },
|
||||
imageVector = Icons.Outlined.FilterAlt,
|
||||
contentDescription = "分类"
|
||||
)
|
||||
|
||||
TopBarActionButton(
|
||||
onClick = { viewModel.onIntent(ExploreShowIntent.ToggleLayout) },
|
||||
imageVector = if (!isGridMode) Icons.AutoMirrored.Outlined.FormatListBulleted else Icons.Default.GridView,
|
||||
contentDescription = "切换布局"
|
||||
)
|
||||
@@ -288,8 +270,8 @@ fun ExploreShowScreen(
|
||||
) { paddingValues ->
|
||||
AppPullToRefresh(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = { viewModel.loadMore(isRefresh = true) },
|
||||
isRefreshing = state.isRefreshing,
|
||||
onRefresh = { viewModel.onIntent(ExploreShowIntent.Refresh) },
|
||||
topPadding = paddingValues.calculateTopPadding()
|
||||
) {
|
||||
Crossfade(
|
||||
@@ -303,7 +285,7 @@ fun ExploreShowScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.responsiveHazeSource(hazeState),
|
||||
columns = GridCells.Fixed(gridColumnCount),
|
||||
columns = GridCells.Fixed(state.gridCount),
|
||||
contentPadding = PaddingValues(
|
||||
top = paddingValues.calculateTopPadding() + 12.dp,
|
||||
bottom = paddingValues.calculateBottomPadding() + 12.dp,
|
||||
@@ -324,7 +306,14 @@ fun ExploreShowScreen(
|
||||
ExploreBookGridItem(
|
||||
book = item.book,
|
||||
shelfState = item.shelfState,
|
||||
onClick = { onBookClick(item.book, sharedCoverKey) },
|
||||
onClick = {
|
||||
viewModel.onIntent(
|
||||
ExploreShowIntent.OpenBook(
|
||||
item.book,
|
||||
sharedCoverKey
|
||||
)
|
||||
)
|
||||
},
|
||||
onLongClick = { book, coverKey ->
|
||||
previewBook = book
|
||||
previewSharedCoverKey = coverKey
|
||||
@@ -338,10 +327,10 @@ fun ExploreShowScreen(
|
||||
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
LoadMoreFooter(
|
||||
isLoading = isLoading,
|
||||
errorMsg = errorMsg,
|
||||
isEnd = isBookEnd,
|
||||
onRetry = viewModel::loadMore
|
||||
isLoading = state.isLoading,
|
||||
errorMsg = state.errorMsg,
|
||||
isEnd = state.isEnd,
|
||||
onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -367,7 +356,14 @@ fun ExploreShowScreen(
|
||||
ExploreBookItem(
|
||||
book = item.book,
|
||||
shelfState = item.shelfState,
|
||||
onClick = { onBookClick(item.book, sharedCoverKey) },
|
||||
onClick = {
|
||||
viewModel.onIntent(
|
||||
ExploreShowIntent.OpenBook(
|
||||
item.book,
|
||||
sharedCoverKey
|
||||
)
|
||||
)
|
||||
},
|
||||
onLongClick = { book, coverKey ->
|
||||
previewBook = book
|
||||
previewSharedCoverKey = coverKey
|
||||
@@ -381,15 +377,14 @@ fun ExploreShowScreen(
|
||||
|
||||
item {
|
||||
LoadMoreFooter(
|
||||
isLoading = isLoading,
|
||||
errorMsg = errorMsg,
|
||||
isEnd = isBookEnd,
|
||||
onRetry = viewModel::loadMore
|
||||
isLoading = state.isLoading,
|
||||
errorMsg = state.errorMsg,
|
||||
isEnd = state.isEnd,
|
||||
onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,7 +403,7 @@ fun ExploreShowScreen(
|
||||
onBookClick(book, sharedCoverKey)
|
||||
},
|
||||
onAddToShelf = { book ->
|
||||
viewModel.onAddToShelf(book)
|
||||
viewModel.onIntent(ExploreShowIntent.AddToShelf(book))
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -454,10 +449,9 @@ fun ExploreBookGridItem(
|
||||
shelfState = shelfState,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
modifier = modifier,
|
||||
modifier = modifier.padding(4.dp),
|
||||
sharedTransitionScope = sharedTransitionScope,
|
||||
animatedVisibilityScope = animatedVisibilityScope,
|
||||
sharedCoverKey = sharedCoverKey
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.data.entities.rule.ExploreKind
|
||||
import io.legado.app.data.repository.ExploreRepository
|
||||
import io.legado.app.domain.model.BookShelfState
|
||||
import io.legado.app.domain.usecase.AddToBookshelfUseCase
|
||||
import io.legado.app.domain.usecase.BookShelfKey
|
||||
import io.legado.app.domain.usecase.ExploreBooksUseCase
|
||||
@@ -15,43 +14,16 @@ import io.legado.app.domain.usecase.SaveSearchBooksUseCase
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.utils.exploreLayoutGrid
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
|
||||
sealed class BookFilterState(val id: Int) {
|
||||
data object SHOW_ALL : BookFilterState(0)
|
||||
data object HIDE_IN_SHELF : BookFilterState(1)
|
||||
data object HIDE_SAME_NAME_AUTHOR : BookFilterState(2)
|
||||
data object SHOW_NOT_IN_SHELF_ONLY : BookFilterState(3)
|
||||
|
||||
companion object {
|
||||
fun fromId(id: Int): BookFilterState = when (id) {
|
||||
1 -> HIDE_IN_SHELF
|
||||
2 -> HIDE_SAME_NAME_AUTHOR
|
||||
3 -> SHOW_NOT_IN_SHELF_ONLY
|
||||
else -> SHOW_ALL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UiState<out T> {
|
||||
data object Loading : UiState<Nothing>()
|
||||
data class Success<T>(val data: T) : UiState<T>()
|
||||
data class Error(val message: String) : UiState<Nothing>()
|
||||
data object Empty : UiState<Nothing>()
|
||||
}
|
||||
|
||||
data class ExploreBookItemUi(
|
||||
val book: SearchBook,
|
||||
val shelfState: BookShelfState = BookShelfState.NOT_IN_SHELF,
|
||||
)
|
||||
|
||||
class ExploreShowViewModel(
|
||||
private val repository: ExploreRepository,
|
||||
private val resolveBookShelfStateUseCase: ResolveBookShelfStateUseCase,
|
||||
@@ -61,72 +33,128 @@ class ExploreShowViewModel(
|
||||
) : ViewModel() {
|
||||
|
||||
private val _rawBooks = MutableStateFlow<List<SearchBook>>(emptyList())
|
||||
private val _bookshelf = MutableStateFlow<Set<BookShelfKey>>(emptySet())
|
||||
private val _isLoading = MutableStateFlow(false)
|
||||
private val _isRefreshing = MutableStateFlow(false)
|
||||
private val _isEnd = MutableStateFlow(false)
|
||||
private val _errorMsg = MutableStateFlow<String?>(null)
|
||||
private val _kinds = MutableStateFlow<List<ExploreKind>>(emptyList())
|
||||
private val _selectedKindTitle = MutableStateFlow<String?>(null)
|
||||
|
||||
private var bookSource: BookSource? = null
|
||||
private var sourceUrl: String? = null
|
||||
private var exploreUrl: String? = null
|
||||
private var page = 1
|
||||
private val _isEndStateFlow = MutableStateFlow(false)
|
||||
private val _bookshelf = MutableStateFlow<Set<BookShelfKey>>(emptySet())
|
||||
private val _kinds = MutableStateFlow<List<ExploreKind>>(emptyList())
|
||||
val kinds = _kinds.asStateFlow()
|
||||
private val _selectedKindTitle = MutableStateFlow<String?>(null)
|
||||
val selectedKindTitle = _selectedKindTitle.asStateFlow()
|
||||
private val _layoutState = MutableStateFlow(AppConfig.exploreLayoutState) // 0=列表, 1=网格
|
||||
val layoutState: StateFlow<Int> = _layoutState.asStateFlow()
|
||||
private val _gridCount = MutableStateFlow(appCtx.exploreLayoutGrid)
|
||||
val gridCount = _gridCount.asStateFlow()
|
||||
val isEnd: StateFlow<Boolean> = _isEndStateFlow.asStateFlow()
|
||||
|
||||
val isRefreshing = _isRefreshing.asStateFlow()
|
||||
private val _uiState = MutableStateFlow(
|
||||
ExploreShowUiState(
|
||||
layoutState = AppConfig.exploreLayoutState,
|
||||
gridCount = appCtx.exploreLayoutGrid,
|
||||
)
|
||||
)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
fun saveGridCount(value: Int) {
|
||||
appCtx.exploreLayoutGrid = value
|
||||
_gridCount.value = value
|
||||
}
|
||||
|
||||
val uiBooks = combine(
|
||||
_rawBooks,
|
||||
_bookshelf
|
||||
) { books, bookshelf ->
|
||||
books.map { item ->
|
||||
ExploreBookItemUi(
|
||||
book = item,
|
||||
shelfState = resolveBookShelfStateUseCase.execute(
|
||||
name = item.name,
|
||||
author = item.author,
|
||||
url = item.bookUrl,
|
||||
shelf = bookshelf
|
||||
)
|
||||
)
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||
|
||||
val shouldTriggerAutoLoad = combine(
|
||||
_isLoading,
|
||||
uiBooks,
|
||||
_rawBooks,
|
||||
_isEndStateFlow
|
||||
) { loading, uiBooks, rawBooks, isEnd ->
|
||||
!loading && uiBooks.isEmpty() && rawBooks.isNotEmpty() && !isEnd
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
|
||||
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
val errorMsg = _errorMsg.asStateFlow()
|
||||
private val _effects = MutableSharedFlow<ExploreShowEffect>(extraBufferCapacity = 16)
|
||||
val effects = _effects.asSharedFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
repository.getBookshelfItems().collect { list ->
|
||||
val keys = list.map { BookShelfKey(it.name, it.author, it.bookUrl) }.toSet()
|
||||
_bookshelf.value = keys
|
||||
observeBookshelf()
|
||||
combineUiState()
|
||||
}
|
||||
|
||||
fun onIntent(intent: ExploreShowIntent) {
|
||||
when (intent) {
|
||||
is ExploreShowIntent.InitData -> initData(intent.sourceUrl, intent.exploreUrl)
|
||||
ExploreShowIntent.LoadMore -> loadMore()
|
||||
ExploreShowIntent.Refresh -> loadMore(isRefresh = true)
|
||||
is ExploreShowIntent.SwitchKind -> switchKind(intent.kind)
|
||||
ExploreShowIntent.ToggleLayout -> toggleLayout()
|
||||
is ExploreShowIntent.SaveGridCount -> saveGridCount(intent.count)
|
||||
is ExploreShowIntent.ShowSheet -> _uiState.update { it.copy(sheet = intent.sheet) }
|
||||
ExploreShowIntent.DismissSheet -> _uiState.update { it.copy(sheet = ExploreShowSheet.None) }
|
||||
is ExploreShowIntent.OpenBook -> emitEffect(
|
||||
ExploreShowEffect.OpenBookInfo(
|
||||
name = intent.book.name,
|
||||
author = intent.book.author,
|
||||
bookUrl = intent.book.bookUrl,
|
||||
origin = intent.book.origin,
|
||||
coverPath = intent.book.coverUrl,
|
||||
sharedCoverKey = intent.sharedCoverKey,
|
||||
)
|
||||
)
|
||||
|
||||
is ExploreShowIntent.AddToShelf -> viewModelScope.launch {
|
||||
addToBookshelfUseCase.execute(intent.book)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun initData(incomingSourceUrl: String?, incomingExploreUrl: String?) {
|
||||
// 允许 incomingExploreUrl 为空,此时加载书源默认发现页
|
||||
private fun observeBookshelf() {
|
||||
viewModelScope.launch {
|
||||
repository.getBookshelfItems().collect { list ->
|
||||
_bookshelf.value = list.map {
|
||||
BookShelfKey(it.name, it.author, it.bookUrl)
|
||||
}.toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun combineUiState() {
|
||||
viewModelScope.launch {
|
||||
combine(
|
||||
_rawBooks,
|
||||
_bookshelf,
|
||||
_isLoading,
|
||||
_isEnd,
|
||||
_errorMsg,
|
||||
_kinds,
|
||||
_selectedKindTitle,
|
||||
) { values ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val rawBooks = values[0] as List<SearchBook>
|
||||
val bookshelf = values[1] as Set<BookShelfKey>
|
||||
val isLoading = values[2] as Boolean
|
||||
val isEnd = values[3] as Boolean
|
||||
val errorMsg = values[4] as String?
|
||||
val kinds = values[5] as List<ExploreKind>
|
||||
val selectedKindTitle = values[6] as String?
|
||||
|
||||
val books = rawBooks.map { item ->
|
||||
ExploreBookItemUi(
|
||||
book = item,
|
||||
shelfState = resolveBookShelfStateUseCase.execute(
|
||||
name = item.name,
|
||||
author = item.author,
|
||||
url = item.bookUrl,
|
||||
shelf = bookshelf,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ExploreShowUiState(
|
||||
sourceUrl = sourceUrl,
|
||||
books = books.toImmutableList(),
|
||||
kinds = kinds.toImmutableList(),
|
||||
selectedKindTitle = selectedKindTitle,
|
||||
layoutState = _uiState.value.layoutState,
|
||||
gridCount = _uiState.value.gridCount,
|
||||
isLoading = isLoading,
|
||||
isRefreshing = isLoading && page == 1,
|
||||
isEnd = isEnd,
|
||||
errorMsg = errorMsg,
|
||||
sheet = _uiState.value.sheet,
|
||||
)
|
||||
}.collect { newState ->
|
||||
val oldState = _uiState.value
|
||||
_uiState.value = newState
|
||||
|
||||
if (newState.books.isEmpty() && _rawBooks.value.isNotEmpty() && !newState.isEnd && !newState.isLoading) {
|
||||
loadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initData(incomingSourceUrl: String?, incomingExploreUrl: String?) {
|
||||
if (sourceUrl == incomingSourceUrl && exploreUrl == incomingExploreUrl && bookSource != null) {
|
||||
return
|
||||
}
|
||||
@@ -135,16 +163,16 @@ class ExploreShowViewModel(
|
||||
page = 1
|
||||
bookSource = null
|
||||
_rawBooks.value = emptyList()
|
||||
_isEndStateFlow.value = false
|
||||
_isEnd.value = false
|
||||
_errorMsg.value = null
|
||||
_selectedKindTitle.value = null
|
||||
_kinds.value = emptyList()
|
||||
|
||||
viewModelScope.launch {
|
||||
if (bookSource == null && incomingSourceUrl != null) {
|
||||
bookSource = repository.getBookSource(incomingSourceUrl)
|
||||
}
|
||||
|
||||
// 如果仍然没有发现 URL,且书源已加载,尝试使用书源的默认发现页
|
||||
if (exploreUrl == null && bookSource != null) {
|
||||
loadKinds(incomingSourceUrl!!)
|
||||
}
|
||||
@@ -153,33 +181,36 @@ class ExploreShowViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun loadKinds(sourceUrl: String) {
|
||||
private fun loadKinds(sourceUrl: String) {
|
||||
viewModelScope.launch {
|
||||
_kinds.value = repository.getSourceExploreKinds(sourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshKinds() {
|
||||
sourceUrl?.let { loadKinds(it) }
|
||||
}
|
||||
|
||||
fun switchExploreUrl(kind: ExploreKind) {
|
||||
private fun switchKind(kind: ExploreKind) {
|
||||
_selectedKindTitle.value = kind.title
|
||||
exploreUrl = kind.url
|
||||
_isEndStateFlow.value = false
|
||||
_isEnd.value = false
|
||||
loadMore(isRefresh = true)
|
||||
}
|
||||
|
||||
fun setLayout() {
|
||||
val newState = if (_layoutState.value == 0) 1 else 0
|
||||
_layoutState.value = newState
|
||||
AppConfig.exploreLayoutState = newState
|
||||
private fun toggleLayout() {
|
||||
_uiState.update {
|
||||
val newState = if (it.layoutState == 0) 1 else 0
|
||||
AppConfig.exploreLayoutState = newState
|
||||
it.copy(layoutState = newState)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadMore(isRefresh: Boolean = false) {
|
||||
private fun saveGridCount(count: Int) {
|
||||
appCtx.exploreLayoutGrid = count
|
||||
_uiState.update { it.copy(gridCount = count) }
|
||||
}
|
||||
|
||||
private fun loadMore(isRefresh: Boolean = false) {
|
||||
val source = bookSource
|
||||
val url = exploreUrl ?: source?.exploreUrl
|
||||
if (source == null || url == null || _isLoading.value || (_isEndStateFlow.value && !isRefresh)) return
|
||||
if (source == null || url == null || _isLoading.value || (_isEnd.value && !isRefresh)) return
|
||||
|
||||
viewModelScope.launch {
|
||||
_isLoading.value = true
|
||||
@@ -187,7 +218,7 @@ class ExploreShowViewModel(
|
||||
|
||||
if (isRefresh) {
|
||||
page = 1
|
||||
_isEndStateFlow.value = false
|
||||
_isEnd.value = false
|
||||
_rawBooks.value = emptyList()
|
||||
}
|
||||
|
||||
@@ -195,7 +226,7 @@ class ExploreShowViewModel(
|
||||
exploreBooksUseCase.execute(source.bookSourceUrl, url, args = null, page)
|
||||
}.onSuccess { result ->
|
||||
if (result.books.isEmpty()) {
|
||||
_isEndStateFlow.value = true
|
||||
_isEnd.value = true
|
||||
} else {
|
||||
saveSearchBooksUseCase.save(result.books)
|
||||
|
||||
@@ -207,34 +238,22 @@ class ExploreShowViewModel(
|
||||
.distinctBy { it.bookUrl }
|
||||
|
||||
if (uniqueNewBooks.isEmpty()) {
|
||||
_isEndStateFlow.value = true
|
||||
_isEnd.value = true
|
||||
} else {
|
||||
_rawBooks.value = currentList + uniqueNewBooks
|
||||
page++
|
||||
_isEndStateFlow.value = false
|
||||
_isEnd.value = false
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
_errorMsg.value = it.stackTraceStr
|
||||
}
|
||||
.onFailure {
|
||||
_errorMsg.value = it.stackTraceStr
|
||||
}
|
||||
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentBookShelfState(item: SearchBook): BookShelfState {
|
||||
return resolveBookShelfStateUseCase.execute(
|
||||
name = item.name,
|
||||
author = item.author,
|
||||
url = item.bookUrl,
|
||||
shelf = _bookshelf.value
|
||||
)
|
||||
}
|
||||
|
||||
fun onAddToShelf(book: SearchBook) {
|
||||
viewModelScope.launch {
|
||||
addToBookshelfUseCase.execute(book)
|
||||
}
|
||||
private fun emitEffect(effect: ExploreShowEffect) {
|
||||
_effects.tryEmit(effect)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.image.cover.CoilBookCover
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
@@ -236,11 +236,11 @@ fun GroupDeleteAction(
|
||||
) {
|
||||
var showDeleteDialog by remember { mutableStateOf(false) }
|
||||
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = {
|
||||
showDeleteDialog = true
|
||||
},
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
|
||||
AppAlertDialog(
|
||||
@@ -266,7 +266,7 @@ fun GroupResetCoverAction(
|
||||
onCoverPathChange: (String?) -> Unit,
|
||||
viewModel: GroupViewModel = koinViewModel()
|
||||
) {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = {
|
||||
if (group != null) {
|
||||
viewModel.clearCover(group) {
|
||||
@@ -277,6 +277,6 @@ fun GroupResetCoverAction(
|
||||
onCoverPathChange(null)
|
||||
}
|
||||
},
|
||||
imageVector = Icons.Default.Restore
|
||||
icon = Icons.Default.Restore
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.SelectionActions
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.card.TextCard
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
@@ -427,9 +427,9 @@ private fun ImportPathNavigationBar(
|
||||
}
|
||||
|
||||
if (canGoBack) {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onNavigateBack,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "back"
|
||||
)
|
||||
}
|
||||
@@ -511,9 +511,9 @@ private fun ImportBookItem(
|
||||
|
||||
if (!item.isDir && !item.isOnBookShelf) {
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onAddToBookshelf,
|
||||
imageVector = if (isSelected) Icons.Default.Check else Icons.Default.AddCircleOutline
|
||||
icon = if (isSelected) Icons.Default.Check else Icons.Default.AddCircleOutline
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,9 +76,9 @@ import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.SelectionActions
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
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.TextCard
|
||||
@@ -167,9 +167,9 @@ fun RemoteBookScreen(
|
||||
},
|
||||
endAction = if (showSheet is RemoteBookSheet.Servers) {
|
||||
{
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { showSheet = RemoteBookSheet.ServerConfig(null) },
|
||||
imageVector = Icons.Default.Add
|
||||
icon = Icons.Default.Add
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -430,16 +430,16 @@ private fun ServerItem(
|
||||
{
|
||||
Row {
|
||||
onEdit?.let {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = it,
|
||||
imageVector = Icons.Default.Edit,
|
||||
icon = Icons.Default.Edit,
|
||||
contentDescription = "Edit"
|
||||
)
|
||||
}
|
||||
onDelete?.let {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = it,
|
||||
imageVector = Icons.Default.Delete,
|
||||
icon = Icons.Default.Delete,
|
||||
contentDescription = "Delete"
|
||||
)
|
||||
}
|
||||
@@ -602,9 +602,9 @@ private fun PathNavigationBar(
|
||||
}
|
||||
|
||||
if (canGoBack) {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onNavigateBack,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "返回上级"
|
||||
)
|
||||
}
|
||||
@@ -703,7 +703,7 @@ private fun RemoteBookItem(
|
||||
if (!book.isDir) {
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
if (book.isOnBookShelf) {
|
||||
onUpdateClick(book)
|
||||
@@ -711,7 +711,7 @@ private fun RemoteBookItem(
|
||||
onAddClick(book)
|
||||
}
|
||||
},
|
||||
imageVector = if (book.isOnBookShelf)
|
||||
icon = if (book.isOnBookShelf)
|
||||
Icons.Outlined.CloudSync
|
||||
else
|
||||
Icons.Outlined.AddCircleOutline,
|
||||
|
||||
@@ -89,7 +89,7 @@ import io.legado.app.ui.widget.components.AppPullToRefresh
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.card.TextCard
|
||||
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||
@@ -1122,9 +1122,9 @@ private fun RelatedBooksBanner(
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onMoreClick,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowForward,
|
||||
contentDescription = "more",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
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.checkBox.AppCheckbox
|
||||
@@ -144,15 +144,15 @@ fun GroupSelectSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.group_select),
|
||||
startAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { editingGroup = BookGroup() },
|
||||
imageVector = Icons.Default.Add
|
||||
icon = Icons.Default.Add
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { onConfirm(selectedGroupId) },
|
||||
imageVector = Icons.Default.Check
|
||||
icon = Icons.Default.Check
|
||||
)
|
||||
}
|
||||
) {
|
||||
@@ -186,9 +186,9 @@ fun GroupSelectSheet(
|
||||
)
|
||||
},
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { editingGroup = group },
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
},
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainerLow
|
||||
@@ -228,9 +228,9 @@ fun ChangeCoverSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.change_cover_source),
|
||||
endAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { viewModel.startOrStopSearch() },
|
||||
imageVector = if (isSearching) Icons.Default.MoreVert else Icons.Default.Refresh
|
||||
icon = if (isSearching) Icons.Default.MoreVert else Icons.Default.Refresh
|
||||
)
|
||||
}
|
||||
) {
|
||||
@@ -362,9 +362,9 @@ fun ChangeSourceSheet(
|
||||
startAction = {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Box {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { showOptionsMenu = true },
|
||||
imageVector = Icons.Default.MoreVert
|
||||
icon = Icons.Default.MoreVert
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
expanded = showOptionsMenu,
|
||||
@@ -411,21 +411,21 @@ fun ChangeSourceSheet(
|
||||
)
|
||||
}
|
||||
}
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { showMigrationOptions = true },
|
||||
imageVector = Icons.Outlined.Settings
|
||||
icon = Icons.Outlined.Settings
|
||||
)
|
||||
}
|
||||
},
|
||||
endAction = {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { viewModel.startOrStopSearch() },
|
||||
imageVector = if (isSearching) Icons.Default.PauseCircleOutline else Icons.Default.Refresh,
|
||||
icon = if (isSearching) Icons.Default.PauseCircleOutline else Icons.Default.Refresh,
|
||||
)
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { showFilterSheet = true },
|
||||
imageVector = Icons.Default.FilterList
|
||||
icon = Icons.Default.FilterList
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -476,11 +476,11 @@ fun ChangeSourceSheet(
|
||||
containerColor = LegadoTheme.colorScheme.onSheetContent,
|
||||
selectedContainerColor = LegadoTheme.colorScheme.primaryContainer.copy(alpha = 0.32f),
|
||||
leadingContent = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = {
|
||||
viewModel.onBookScoreClick(item)
|
||||
},
|
||||
imageVector = Icons.Default.PushPin,
|
||||
icon = Icons.Default.PushPin,
|
||||
tint = if (bookScore > 0) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.outline,
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ import io.legado.app.ui.theme.fadingEdge
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.MediumOutlinedIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumOutlinedButton
|
||||
import io.legado.app.ui.widget.components.image.cover.CoilBookCover
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
@@ -156,7 +156,7 @@ fun BookInfoEditContent(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = {
|
||||
(context as? BookInfoEditActivity)?.showDialogFragment(
|
||||
ChangeCoverDialog(
|
||||
@@ -165,15 +165,15 @@ fun BookInfoEditContent(
|
||||
)
|
||||
)
|
||||
},
|
||||
imageVector = Icons.Default.ImageSearch
|
||||
icon = Icons.Default.ImageSearch
|
||||
)
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = { selectCover.launch() },
|
||||
imageVector = Icons.Default.FolderOpen
|
||||
icon = Icons.Default.FolderOpen
|
||||
)
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = { viewModel.resetCover() },
|
||||
imageVector = Icons.Default.Replay
|
||||
icon = Icons.Default.Replay
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
@@ -371,19 +371,19 @@ fun KindEditor(
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = {
|
||||
onReset()
|
||||
},
|
||||
imageVector = Icons.Default.Replay
|
||||
icon = Icons.Default.Replay
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = {
|
||||
editingIndex = -1
|
||||
editText = ""
|
||||
},
|
||||
imageVector = Icons.Default.Add
|
||||
icon = Icons.Default.Add
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,7 @@ import io.legado.app.ui.widget.components.AppFloatingActionButtonMenu
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.FabMenuItem
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalTextButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||
@@ -768,32 +767,32 @@ private fun BookshelfManageScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = {
|
||||
if (!book.isLocal) {
|
||||
viewModel.dispatch(BookshelfManageScreenIntent.ToggleBookDownload(book))
|
||||
}
|
||||
},
|
||||
imageVector = if (isDownloading) Icons.Default.Stop else Icons.Default.Download,
|
||||
icon = if (isDownloading) Icons.Default.Stop else Icons.Default.Download,
|
||||
contentDescription = "download"
|
||||
)
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = { exportBook(book) },
|
||||
imageVector = Icons.Default.Upload,
|
||||
icon = Icons.Default.Upload,
|
||||
contentDescription = "upload"
|
||||
)
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = {
|
||||
pendingMoveGroupBookUrl = book.bookUrl
|
||||
groupPickerCurrentGroupId = book.group.coerceAtLeast(0L)
|
||||
showGroupSelectSheet = true
|
||||
},
|
||||
imageVector = Icons.Default.Bookmarks,
|
||||
icon = Icons.Default.Bookmarks,
|
||||
contentDescription = "group"
|
||||
)
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = { moreMenuBookUrl = book.bookUrl },
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
icon = Icons.Default.MoreVert,
|
||||
contentDescription = "more"
|
||||
)
|
||||
}
|
||||
@@ -1288,12 +1287,12 @@ private fun BookSourcePickerSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = title,
|
||||
endAction = {
|
||||
SmallTonalTextButton(
|
||||
text = stringResource(android.R.string.ok),
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
SmallTonalButton(
|
||||
onClick = {
|
||||
onConfirm(selectedSources.mapNotNull { it.getBookSource() })
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.PlayArrow,
|
||||
text = stringResource(android.R.string.ok)
|
||||
)
|
||||
}
|
||||
) {
|
||||
@@ -1402,17 +1401,17 @@ private fun BatchChangePreviewSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = "批量换源预览",
|
||||
startAction = {
|
||||
SmallTonalTextButton(
|
||||
text = "新增全部",
|
||||
imageVector = Icons.Default.Add,
|
||||
SmallTonalButton(
|
||||
onClick = onAddAllToShelf,
|
||||
icon = Icons.Default.Add,
|
||||
text = "新增全部"
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
SmallTonalTextButton(
|
||||
text = "迁移全部",
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
SmallTonalButton(
|
||||
onClick = onMigrateAll,
|
||||
icon = Icons.Default.PlayArrow,
|
||||
text = "迁移全部"
|
||||
)
|
||||
}
|
||||
) { items ->
|
||||
@@ -1498,30 +1497,30 @@ private fun BatchChangePreviewRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
SmallTonalTextButton(
|
||||
imageVector = Icons.Default.Search,
|
||||
SmallTonalButton(
|
||||
onClick = { onManualSearch(item.oldBook) },
|
||||
icon = Icons.Default.Search
|
||||
)
|
||||
SmallTonalTextButton(
|
||||
text = "不迁移",
|
||||
imageVector = Icons.Default.Clear,
|
||||
SmallTonalButton(
|
||||
onClick = { onSkip(item.oldBook.bookUrl) },
|
||||
icon = Icons.Default.Clear,
|
||||
text = "不迁移"
|
||||
)
|
||||
SmallTonalTextButton(
|
||||
text = "迁移",
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
SmallTonalButton(
|
||||
onClick = { onMigrate(item.oldBook.bookUrl) },
|
||||
icon = Icons.Default.PlayArrow,
|
||||
text = "迁移"
|
||||
)
|
||||
SmallTonalTextButton(
|
||||
text = "新增",
|
||||
imageVector = Icons.Default.Add,
|
||||
SmallTonalButton(
|
||||
onClick = { onAddToShelf(item.oldBook.bookUrl) },
|
||||
icon = Icons.Default.Add,
|
||||
text = "新增"
|
||||
)
|
||||
if (item.candidates.size > 1) {
|
||||
SmallTonalTextButton(
|
||||
text = "查看其他源信息",
|
||||
imageVector = Icons.Default.Info,
|
||||
SmallTonalButton(
|
||||
onClick = { onShowOtherSources(item) },
|
||||
icon = Icons.Default.Info,
|
||||
text = "查看其他源信息"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1605,9 +1604,9 @@ private fun OtherSourceOptionsSheet(
|
||||
isSelected = index == currentItem.selectedCandidateIndex,
|
||||
onToggleSelection = { onSelect(currentItem.oldBook.bookUrl, index) },
|
||||
trailingAction = {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = { onOpenBook(candidate.book) },
|
||||
imageVector = Icons.Default.Info,
|
||||
icon = Icons.Default.Info,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@ import io.legado.app.ui.book.readRecord.component.StatsGridCard
|
||||
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.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.heatmap.HeatmapMode
|
||||
import io.legado.app.ui.widget.components.image.cover.CoilBookCover
|
||||
@@ -187,9 +187,9 @@ fun DateNavigator(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = onPrevClick,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowLeft
|
||||
icon = Icons.AutoMirrored.Filled.ArrowLeft
|
||||
)
|
||||
AnimatedContent(
|
||||
targetState = referenceDate,
|
||||
@@ -224,9 +224,9 @@ fun DateNavigator(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = onNextClick,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowRight
|
||||
icon = Icons.AutoMirrored.Filled.ArrowRight
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import io.legado.app.R
|
||||
import io.legado.app.data.entities.BookSourcePart
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.button.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
|
||||
@@ -71,7 +71,7 @@ import io.legado.app.ui.widget.components.SearchBar
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.book.SearchBookListItem
|
||||
import io.legado.app.ui.widget.components.book.SearchBookPreviewSheet
|
||||
import io.legado.app.ui.widget.components.button.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
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.icon.AppIcon
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package io.legado.app.ui.book.search
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibilityScope
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.SharedTransitionScope
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.ui.main.bookCoverSharedElementKey
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.adaptiveHorizonalPadding
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||
import io.legado.app.ui.theme.fadingEdge
|
||||
import io.legado.app.ui.widget.components.book.SearchBookGridItem
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
import io.legado.app.ui.widget.components.card.TextCard
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
fun SearchSourceSection(
|
||||
sourceName: String,
|
||||
items: List<SearchResultItemUi>,
|
||||
onClickBook: (SearchBook, String?) -> Unit,
|
||||
onLongClickBook: ((SearchBook, String?) -> Unit)? = null,
|
||||
onViewAll: (() -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
sharedTransitionScope: SharedTransitionScope? = null,
|
||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||
sourceSectionIndex: Int = 0,
|
||||
) {
|
||||
if (items.isEmpty()) return
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.adaptiveHorizontalPadding()
|
||||
.padding(vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
AppText(
|
||||
text = sourceName,
|
||||
style = LegadoTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
TextCard(
|
||||
text = "${items.size}",
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (onViewAll != null) {
|
||||
SmallTonalButton(
|
||||
onClick = onViewAll,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowForward
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
LazyRow(
|
||||
state = lazyListState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fadingEdge(lazyListState, gradientWidth = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
contentPadding = adaptiveHorizonalPadding(),
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = items,
|
||||
key = { index, item -> "${item.book.origin}:${item.book.bookUrl}:$index" }
|
||||
) { index, item ->
|
||||
val sharedCoverKey = bookCoverSharedElementKey(
|
||||
item.book.bookUrl,
|
||||
"search_source:$sourceSectionIndex:$index"
|
||||
)
|
||||
SearchBookGridItem(
|
||||
book = item.book,
|
||||
shelfState = item.shelfState,
|
||||
onClick = { onClickBook(item.book, sharedCoverKey) },
|
||||
onLongClick = onLongClickBook,
|
||||
modifier = Modifier.width(100.dp),
|
||||
sharedTransitionScope = sharedTransitionScope,
|
||||
animatedVisibilityScope = animatedVisibilityScope,
|
||||
sharedCoverKey = sharedCoverKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,10 +59,10 @@ import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
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.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.SmallToggleButton
|
||||
import io.legado.app.ui.widget.components.button.ToggleStyle
|
||||
import io.legado.app.ui.widget.components.button.series.MediumOutlinedButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallToggleButton
|
||||
import io.legado.app.ui.widget.components.button.series.ToggleStyle
|
||||
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.lazylist.FastScrollLazyColumn
|
||||
|
||||
@@ -95,7 +95,8 @@ import io.legado.app.ui.widget.components.FabMenuItem
|
||||
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.button.series.SmallToggleButton
|
||||
import io.legado.app.ui.widget.components.button.series.ToggleStyle
|
||||
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
|
||||
@@ -476,10 +477,11 @@ fun TocScreen(
|
||||
|
||||
if (pagerState.currentPage == 0 && hasVolumes) {
|
||||
Box {
|
||||
SmallOutlinedIconToggleButton(
|
||||
SmallToggleButton(
|
||||
checked = showVolumeMenu,
|
||||
onCheckedChange = { showVolumeMenu = it },
|
||||
imageVector = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||
style = ToggleStyle.Outlined,
|
||||
icon = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||
contentDescription = stringResource(R.string.volume_management)
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
|
||||
@@ -5,24 +5,15 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.FileOpen
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -48,7 +39,7 @@ import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.ActionItem
|
||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
@@ -59,7 +50,6 @@ import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
import io.legado.app.ui.widget.components.rules.RuleEditFields
|
||||
import io.legado.app.ui.widget.components.rules.RuleEditSheet
|
||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
|
||||
@@ -350,9 +340,9 @@ fun TxtRuleScreen(
|
||||
onEnabledChange = { enabled -> viewModel.update(item.rule.copy(enable = enabled)) },
|
||||
onClickEdit = { editingRule = item.rule; showEditSheet = true },
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { showDeleteRuleDialog = item.rule },
|
||||
imageVector = AppIcons.Delete
|
||||
icon = AppIcons.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.legado.app.R
|
||||
import io.legado.app.help.DefaultData
|
||||
import io.legado.app.model.BookCover
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.utils.toastOnUi
|
||||
@@ -59,8 +59,8 @@ fun CoverRuleConfigSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.cover_rule),
|
||||
startAction = {
|
||||
MediumIconButton(
|
||||
imageVector = Icons.Default.SettingsBackupRestore,
|
||||
MediumPlainButton(
|
||||
icon = Icons.Default.SettingsBackupRestore,
|
||||
onClick = {
|
||||
DefaultData.coverRule.let {
|
||||
enable = it.enable
|
||||
@@ -72,8 +72,8 @@ fun CoverRuleConfigSheet(
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
MediumIconButton(
|
||||
imageVector = Icons.Default.Save,
|
||||
MediumPlainButton(
|
||||
icon = Icons.Default.Save,
|
||||
onClick = {
|
||||
if (searchUrl.isBlank() || coverRule.isBlank()) {
|
||||
appCtx.toastOnUi(R.string.cover_rule_fields_required)
|
||||
|
||||
+5
-10
@@ -2,7 +2,6 @@ package io.legado.app.ui.config.otherConfig
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -16,12 +15,8 @@ import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.ContentPaste
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -39,7 +34,7 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
@@ -70,18 +65,18 @@ fun DirectLinkUploadBottomSheet(
|
||||
show = show,
|
||||
title = stringResource(R.string.direct_link_upload_config),
|
||||
startAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = {
|
||||
viewModel.testRule { result -> showTestResult = result }
|
||||
},
|
||||
imageVector = Icons.Default.Checklist
|
||||
icon = Icons.Default.Checklist
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
Box {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { showMenu = true },
|
||||
imageVector = Icons.Default.MoreVert
|
||||
icon = Icons.Default.MoreVert
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
expanded = showMenu,
|
||||
|
||||
@@ -27,7 +27,7 @@ import androidx.compose.ui.unit.dp
|
||||
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.button.series.SmallTonalButton
|
||||
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
|
||||
@@ -114,13 +114,13 @@ fun BackgroundImageManageSheet(
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = { viewModel.removeBackground(isDark) },
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(8.dp)
|
||||
.size(32.dp),
|
||||
imageVector = Icons.Default.Close
|
||||
icon = Icons.Default.Close
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,19 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.AutoAwesome
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -30,8 +28,8 @@ import androidx.core.graphics.ColorUtils
|
||||
import io.legado.app.R
|
||||
import io.legado.app.help.config.TagColorGenerator
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.MediumOutlinedIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumOutlinedButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
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.dialog.ColorPickerSheet
|
||||
@@ -57,7 +55,7 @@ fun LabelColorManageSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.theme_config_manage_label_colors),
|
||||
startAction = {
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = {
|
||||
val baseColor = if (themeColor != 0) Color(themeColor) else primaryColor
|
||||
val generatedColors = TagColorGenerator.generateTagColors(baseColor)
|
||||
@@ -65,23 +63,25 @@ fun LabelColorManageSheet(
|
||||
tagColors.addAll(generatedColors)
|
||||
ThemeConfig.saveCustomTagColors(tagColors)
|
||||
},
|
||||
imageVector = Icons.Default.AutoAwesome
|
||||
icon = Icons.Default.AutoAwesome
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = {
|
||||
tagColors.add(TagColorPair(0, 0))
|
||||
editingIndex = tagColors.size - 1
|
||||
editingTextColor = 0
|
||||
showColorPicker = true
|
||||
},
|
||||
imageVector = Icons.Default.Add
|
||||
icon = Icons.Default.Add
|
||||
)
|
||||
}
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 24.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 24.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(tagColors.size) { index ->
|
||||
@@ -110,20 +110,20 @@ fun LabelColorManageSheet(
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
editingIndex = index
|
||||
editingTextColor = colorPair.textColor
|
||||
showColorPicker = true
|
||||
},
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
tagColors.removeAt(index)
|
||||
ThemeConfig.saveCustomTagColors(tagColors)
|
||||
},
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import androidx.compose.ui.unit.dp
|
||||
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.button.series.SmallTonalButton
|
||||
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.modalBottomSheet.AppModalBottomSheet
|
||||
@@ -123,13 +123,13 @@ fun NavIconManageSheet(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Fit
|
||||
)
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = { dest.onSetPath("") },
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(4.dp)
|
||||
.size(24.dp),
|
||||
imageVector = Icons.Default.Close
|
||||
icon = Icons.Default.Close
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -88,7 +88,7 @@ import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
@@ -222,8 +222,8 @@ fun ThemeConfigScreen(
|
||||
style = LegadoTheme.typography.labelLargeEmphasized,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
SmallIconButton(
|
||||
imageVector = AppIcons.Close,
|
||||
SmallPlainButton(
|
||||
icon = AppIcons.Close,
|
||||
contentDescription = "关闭",
|
||||
onClick = {
|
||||
viewModel.setShowThemeRefactorTip(false)
|
||||
@@ -759,8 +759,8 @@ fun ThemeConfigScreen(
|
||||
onDismissRequest = { showFontSheet = false },
|
||||
title = stringResource(R.string.font_setting),
|
||||
startAction = {
|
||||
SmallIconButton(
|
||||
imageVector = Icons.Default.Delete,
|
||||
SmallPlainButton(
|
||||
icon = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.clear),
|
||||
onClick = {
|
||||
ThemeConfig.appFontPath = null
|
||||
@@ -769,8 +769,8 @@ fun ThemeConfigScreen(
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
SmallIconButton(
|
||||
imageVector = Icons.Default.Add,
|
||||
SmallPlainButton(
|
||||
icon = Icons.Default.Add,
|
||||
contentDescription = stringResource(R.string.select_folder),
|
||||
onClick = { fontFolderLauncher.launch(null) }
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.help.config.ThemeExportData
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.ui.widget.components.settingItem.CompactClickableSettingItem
|
||||
@@ -60,13 +60,13 @@ fun EditThemeSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.theme_manage_edit_theme),
|
||||
endAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = {
|
||||
if (name.isNotBlank()) {
|
||||
onSave(name, data)
|
||||
}
|
||||
},
|
||||
imageVector = Icons.Default.Done,
|
||||
icon = Icons.Default.Done,
|
||||
contentDescription = "Save"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
@@ -393,17 +393,17 @@ private fun SavedThemeItem(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onEdit,
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onExport,
|
||||
imageVector = Icons.Default.Share
|
||||
icon = Icons.Default.Share
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onDelete,
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.ActionItem
|
||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
@@ -315,9 +315,9 @@ fun DictRuleScreen(
|
||||
onEnabledChange = { enabled -> viewModel.update(item.rule.copy(enabled = enabled)) },
|
||||
onClickEdit = { editingRule = item.rule; showEditSheet = true },
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { showDeleteRuleDialog = item.rule },
|
||||
imageVector = AppIcons.Delete
|
||||
icon = AppIcons.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -111,7 +111,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
|
||||
context: Context,
|
||||
exploreName: String? = null,
|
||||
sourceUrl: String,
|
||||
exploreUrl: String? = null
|
||||
exploreUrl: String? = null,
|
||||
): Intent = MainIntent.createExploreShowIntent(context, exploreName, sourceUrl, exploreUrl)
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ object MainIntent {
|
||||
context: Context,
|
||||
exploreName: String? = null,
|
||||
sourceUrl: String,
|
||||
exploreUrl: String? = null
|
||||
exploreUrl: String? = null,
|
||||
): Intent {
|
||||
return createLauncherIntent(context).apply {
|
||||
putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_EXPLORE_SHOW)
|
||||
|
||||
@@ -19,7 +19,9 @@ import io.legado.app.ui.about.AboutEffect
|
||||
import io.legado.app.ui.about.AboutScreen
|
||||
import io.legado.app.ui.about.AboutViewModel
|
||||
import io.legado.app.ui.book.cache.manage.BookCacheManageRouteScreen
|
||||
import io.legado.app.ui.book.explore.ExploreShowIntent
|
||||
import io.legado.app.ui.book.explore.ExploreShowScreen
|
||||
import io.legado.app.ui.book.explore.ExploreShowViewModel
|
||||
import io.legado.app.ui.book.import.local.ImportBookScreen
|
||||
import io.legado.app.ui.book.import.remote.RemoteBookScreen
|
||||
import io.legado.app.ui.book.info.BookInfoRouteScreen
|
||||
@@ -39,8 +41,8 @@ import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigScreen
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfigScreen
|
||||
import io.legado.app.ui.config.readConfig.ReadConfigScreen
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
|
||||
import io.legado.app.ui.config.translation.TranslationConfigScreen
|
||||
import io.legado.app.ui.config.themeManage.ThemeManageScreen
|
||||
import io.legado.app.ui.config.translation.TranslationConfigScreen
|
||||
import io.legado.app.ui.rss.article.MainRouteRssSort
|
||||
import io.legado.app.ui.rss.article.RssSortRouteScreen
|
||||
import io.legado.app.ui.rss.favorites.RssFavoritesScreen
|
||||
@@ -436,10 +438,17 @@ fun MainActivity.mainEntryProvider(
|
||||
}
|
||||
|
||||
entry<MainRouteExploreShow> { route ->
|
||||
val exploreViewModel = koinViewModel<ExploreShowViewModel>()
|
||||
|
||||
LaunchedEffect(route.sourceUrl, route.exploreUrl, exploreViewModel) {
|
||||
exploreViewModel.onIntent(
|
||||
ExploreShowIntent.InitData(route.sourceUrl, route.exploreUrl)
|
||||
)
|
||||
}
|
||||
|
||||
ExploreShowScreen(
|
||||
viewModel = exploreViewModel,
|
||||
title = route.title ?: "探索",
|
||||
sourceUrl = route.sourceUrl,
|
||||
exploreUrl = route.exploreUrl,
|
||||
onBack = { onNavigateBack() },
|
||||
onBookClick = { book, sharedCoverKey ->
|
||||
onNavigateToRoute(
|
||||
|
||||
@@ -268,7 +268,7 @@ object MainNavigator {
|
||||
MainRouteExploreShow(
|
||||
title = intent.getStringExtra(MainIntent.EXTRA_EXPLORE_NAME),
|
||||
sourceUrl = sourceUrl,
|
||||
exploreUrl = intent.getStringExtra(MainIntent.EXTRA_EXPLORE_URL)
|
||||
exploreUrl = intent.getStringExtra(MainIntent.EXTRA_EXPLORE_URL),
|
||||
)
|
||||
} ?: MainRouteHome
|
||||
|
||||
|
||||
@@ -114,7 +114,9 @@ import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
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.SmallOutlinedIconToggleButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallToggleButton
|
||||
import io.legado.app.ui.widget.components.button.series.ToggleStyle
|
||||
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.PillHeaderDivider
|
||||
@@ -557,7 +559,7 @@ fun BookshelfScreen(
|
||||
val showExpandButton by BookshelfConfig.shouldShowExpandButtonState
|
||||
if (showExpandButton) {
|
||||
Box(modifier = Modifier) {
|
||||
SmallOutlinedIconToggleButton(
|
||||
SmallToggleButton(
|
||||
checked = showGroupMenu,
|
||||
onCheckedChange = {
|
||||
if (it) {
|
||||
@@ -566,7 +568,8 @@ fun BookshelfScreen(
|
||||
viewModel.dismissOverlay()
|
||||
}
|
||||
},
|
||||
imageVector = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||
style = ToggleStyle.Outlined,
|
||||
icon = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||
contentDescription = stringResource(R.string.group_manage)
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
@@ -837,7 +840,7 @@ fun BookshelfScreen(
|
||||
if (this != null) Modifier.skipToLookaheadSize() else Modifier
|
||||
}
|
||||
),
|
||||
beyondViewportPageCount = 1,
|
||||
beyondViewportPageCount = 0,
|
||||
key = { if (it < uiState.groups.size) uiState.groups[it].groupId else it }
|
||||
) { pageIndex ->
|
||||
val group = uiState.groups.getOrNull(pageIndex)
|
||||
@@ -1058,9 +1061,10 @@ private fun BookshelfTopBar(
|
||||
},
|
||||
trailingIcon = {
|
||||
if (uiState.searchKey.isNotEmpty()) {
|
||||
TopBarActionButton(
|
||||
SmallPlainButton(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
onClick = onClearSearch,
|
||||
imageVector = AppIcons.Close,
|
||||
icon = AppIcons.Close,
|
||||
contentDescription = stringResource(R.string.clear)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -196,34 +195,31 @@ class BookshelfViewModel(
|
||||
}.distinctUntilChanged().flowOn(Dispatchers.Default)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val allGroupBooksFlow: StateFlow<Map<Long, List<BookUiItem>>> = combine(
|
||||
groupsFlow, sortConfigFlow
|
||||
) { groups, sortConfig ->
|
||||
groups to sortConfig
|
||||
}.flatMapLatest { (groups, sortConfig) ->
|
||||
if (groups.isEmpty()) {
|
||||
flowOf(emptyMap())
|
||||
} else {
|
||||
val flows = groups.map { group ->
|
||||
bookRepository.flowBookShelfByGroup(group.groupId).map { books ->
|
||||
group.groupId to bookshelfRepository.sortBooks(
|
||||
books,
|
||||
group,
|
||||
sortConfig.sort,
|
||||
sortConfig.sortOrder
|
||||
).map { it.toUiItem() }
|
||||
private val allGroupBooksImmutableFlow: StateFlow<ImmutableMap<Long, ImmutableList<BookUiItem>>> =
|
||||
combine(groupsFlow, sortConfigFlow) { groups, sortConfig ->
|
||||
groups to sortConfig
|
||||
}.flatMapLatest { (groups, sortConfig) ->
|
||||
if (groups.isEmpty()) {
|
||||
flowOf(persistentMapOf())
|
||||
} else {
|
||||
val flows = groups.map { group ->
|
||||
bookRepository.flowBookShelfByGroup(group.groupId).map { books ->
|
||||
group.groupId to bookshelfRepository.sortBooks(
|
||||
books,
|
||||
group,
|
||||
sortConfig.sort,
|
||||
sortConfig.sortOrder
|
||||
).map { it.toUiItem() }.toImmutableList()
|
||||
}
|
||||
}
|
||||
combine(flows) { results ->
|
||||
results.fold(persistentMapOf<Long, ImmutableList<BookUiItem>>()) { acc, (id, list) ->
|
||||
acc.put(id, list)
|
||||
}
|
||||
}
|
||||
}
|
||||
combine(flows) { it.toMap() }
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
.flowOn(Dispatchers.Default)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
|
||||
|
||||
private val allGroupBooksImmutableFlow: StateFlow<ImmutableMap<Long, ImmutableList<BookUiItem>>> =
|
||||
allGroupBooksFlow.map { map ->
|
||||
map.mapValues { it.value.toImmutableList() }.toImmutableMap()
|
||||
}.flowOn(Dispatchers.Default)
|
||||
}.distinctUntilChanged()
|
||||
.flowOn(Dispatchers.Default)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), persistentMapOf())
|
||||
|
||||
private val visibleBooksFlow: Flow<List<BookUiItem>> = combine(
|
||||
@@ -301,30 +297,21 @@ class BookshelfViewModel(
|
||||
}
|
||||
}.distinctUntilChanged().flowOn(Dispatchers.Default)
|
||||
|
||||
private val coreInternalStateFlow = combine(
|
||||
private val internalStateFlow = combine(
|
||||
groupIdFlow,
|
||||
searchKeyFlow,
|
||||
searchModeFlow,
|
||||
loadingTextFlow,
|
||||
updatingBooksFlow
|
||||
) { groupId, searchKey, isSearchMode, loadingText, updatingBooks ->
|
||||
combine(updatingBooksFlow, upBooksCountFlow, sortConfigFlow) { a, b, c ->
|
||||
Triple(a, b, c)
|
||||
}
|
||||
) { groupId, searchKey, isSearchMode, loadingText, (updatingBooks, upBooksCount, sortConfig) ->
|
||||
InternalState(
|
||||
groupId = groupId,
|
||||
searchKey = searchKey,
|
||||
isSearchMode = isSearchMode,
|
||||
loadingText = loadingText,
|
||||
updatingBooks = updatingBooks,
|
||||
upBooksCount = 0,
|
||||
sortConfig = readSortConfig()
|
||||
)
|
||||
}
|
||||
|
||||
private val internalStateFlow = combine(
|
||||
coreInternalStateFlow,
|
||||
upBooksCountFlow,
|
||||
sortConfigFlow
|
||||
) { core, upBooksCount, sortConfig ->
|
||||
core.copy(
|
||||
upBooksCount = upBooksCount,
|
||||
sortConfig = sortConfig
|
||||
)
|
||||
@@ -351,35 +338,29 @@ class BookshelfViewModel(
|
||||
val pendingSavedBooks: List<BookUiItem>?
|
||||
)
|
||||
|
||||
private val editStateFlow = combine(
|
||||
private val interactionStateFlow = combine(
|
||||
activeOverlayFlow,
|
||||
isEditModeFlow,
|
||||
selectedVisibleBookUrlsFlow,
|
||||
isInFolderRootFlow
|
||||
) { activeOverlay, isEditMode, selectedBookUrls, isInFolderRoot ->
|
||||
EditState(activeOverlay, isEditMode, selectedBookUrls, isInFolderRoot)
|
||||
}
|
||||
|
||||
private data class EditState(
|
||||
val activeOverlay: BookshelfOverlay?,
|
||||
val isEditMode: Boolean,
|
||||
val selectedBookUrls: Set<String>,
|
||||
val isInFolderRoot: Boolean
|
||||
)
|
||||
|
||||
private val interactionStateFlow = combine(
|
||||
editStateFlow,
|
||||
isRefreshingFlow,
|
||||
bookGroupStyleFlow,
|
||||
draggingBooksFlow,
|
||||
pendingSavedBooksFlow
|
||||
) { editState, isRefreshing, bookGroupStyle, draggingBooks, pendingSavedBooks ->
|
||||
isInFolderRootFlow,
|
||||
isRefreshingFlow
|
||||
) { activeOverlay, isEditMode, selectedBookUrls, isInFolderRoot, isRefreshing ->
|
||||
BookshelfInteractionState(
|
||||
activeOverlay = editState.activeOverlay,
|
||||
isEditMode = editState.isEditMode,
|
||||
selectedBookUrls = editState.selectedBookUrls,
|
||||
isInFolderRoot = editState.isInFolderRoot,
|
||||
activeOverlay = activeOverlay,
|
||||
isEditMode = isEditMode,
|
||||
selectedBookUrls = selectedBookUrls,
|
||||
isInFolderRoot = isInFolderRoot,
|
||||
isRefreshing = isRefreshing,
|
||||
bookGroupStyle = 0,
|
||||
draggingBooks = null,
|
||||
pendingSavedBooks = null
|
||||
)
|
||||
}.combine(
|
||||
combine(bookGroupStyleFlow, draggingBooksFlow, pendingSavedBooksFlow) { a, b, c ->
|
||||
Triple(a, b, c)
|
||||
}
|
||||
) { interaction, (bookGroupStyle, draggingBooks, pendingSavedBooks) ->
|
||||
interaction.copy(
|
||||
bookGroupStyle = bookGroupStyle,
|
||||
draggingBooks = draggingBooks,
|
||||
pendingSavedBooks = pendingSavedBooks
|
||||
@@ -390,11 +371,6 @@ class BookshelfViewModel(
|
||||
GroupPreviewState(persistentMapOf(), persistentMapOf(), 0)
|
||||
)
|
||||
|
||||
private val allGroupBooksStateFlow =
|
||||
MutableStateFlow<ImmutableMap<Long, ImmutableList<BookUiItem>>>(
|
||||
persistentMapOf()
|
||||
)
|
||||
|
||||
private val dataStateFlow = combine(
|
||||
booksFlow,
|
||||
groupsFlow,
|
||||
@@ -403,7 +379,7 @@ class BookshelfViewModel(
|
||||
internalStateFlow
|
||||
) { books, groups, allGroups, previews, internal ->
|
||||
BookshelfDataCore(books, groups, allGroups, previews, internal)
|
||||
}.combine(allGroupBooksStateFlow) { core, allGroupBooks ->
|
||||
}.combine(allGroupBooksImmutableFlow) { core, allGroupBooks ->
|
||||
BookshelfDataState(
|
||||
books = core.books,
|
||||
groups = core.groups.map { it.toBookGroupUi() },
|
||||
@@ -529,9 +505,6 @@ class BookshelfViewModel(
|
||||
viewModelScope.launch {
|
||||
groupPreviewsFlow.collect { groupPreviewsStateFlow.value = it }
|
||||
}
|
||||
viewModelScope.launch {
|
||||
allGroupBooksImmutableFlow.collect { allGroupBooksStateFlow.value = it }
|
||||
}
|
||||
viewModelScope.launch {
|
||||
combine(booksFlow, selectedGroupCanReorderFlow) { books, canReorderBooks ->
|
||||
books to canReorderBooks
|
||||
|
||||
@@ -26,12 +26,12 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.data.entities.BookGroup
|
||||
import io.legado.app.ui.book.group.GroupEditContent
|
||||
import io.legado.app.ui.book.group.GroupDeleteAction
|
||||
import io.legado.app.ui.book.group.GroupEditContent
|
||||
import io.legado.app.ui.book.group.GroupResetCoverAction
|
||||
import io.legado.app.ui.book.group.GroupViewModel
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.utils.move
|
||||
@@ -95,13 +95,13 @@ fun GroupManageSheet(
|
||||
},
|
||||
endAction = {
|
||||
if (!isEditing) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
editingGroup = null
|
||||
coverPath = null
|
||||
isEditing = true
|
||||
},
|
||||
imageVector = Icons.Default.Add
|
||||
icon = Icons.Default.Add
|
||||
)
|
||||
} else {
|
||||
GroupResetCoverAction(
|
||||
|
||||
@@ -92,6 +92,9 @@ fun ExploreScreen(
|
||||
val context = LocalContext.current
|
||||
val activity = context as? AppCompatActivity
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val listItems by remember(uiState.items, uiState.expandedId, uiState.exploreKinds) {
|
||||
derivedStateOf { viewModel.buildExploreListItems(uiState) }
|
||||
}
|
||||
var sourceToDeleteUrl by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val sourceToDelete = remember(sourceToDeleteUrl, uiState.items) {
|
||||
uiState.items.firstOrNull { it.bookSourceUrl == sourceToDeleteUrl }
|
||||
@@ -118,10 +121,10 @@ fun ExploreScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val stickyHeaderSource by remember(uiState.listItems, uiState.items) {
|
||||
val stickyHeaderSource by remember(listItems, uiState.items) {
|
||||
derivedStateOf {
|
||||
val firstIndex = listState.firstVisibleItemIndex
|
||||
val item = uiState.listItems.getOrNull(firstIndex)
|
||||
val item = listItems.getOrNull(firstIndex)
|
||||
if (item is ExploreListItem.KindRow) {
|
||||
uiState.items.find { it.bookSourceUrl == item.sourceUrl }
|
||||
} else {
|
||||
@@ -178,7 +181,7 @@ fun ExploreScreen(
|
||||
)
|
||||
) {
|
||||
items(
|
||||
items = uiState.listItems,
|
||||
items = listItems,
|
||||
key = { it.key }
|
||||
) { listItem ->
|
||||
when (listItem) {
|
||||
@@ -268,7 +271,7 @@ fun ExploreScreen(
|
||||
verticalPadding = 8.dp,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val index = uiState.listItems.indexOfFirst {
|
||||
val index = listItems.indexOfFirst {
|
||||
it is ExploreListItem.Header && it.source.bookSourceUrl == item.bookSourceUrl
|
||||
}
|
||||
if (index >= 0) listState.animateScrollToItem(index)
|
||||
|
||||
@@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -42,7 +41,6 @@ class ExploreViewModel(
|
||||
|
||||
private val _uiState = MutableStateFlow(ExploreUiState())
|
||||
val uiState: StateFlow<ExploreUiState> = _uiState
|
||||
.map { state -> state.copy(listItems = buildExploreListItems(state)) }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ExploreUiState())
|
||||
private val _effects = MutableSharedFlow<ExploreEffect>(extraBufferCapacity = 8)
|
||||
val effects = _effects.asSharedFlow()
|
||||
@@ -200,11 +198,10 @@ class ExploreViewModel(
|
||||
val exploreKinds: ImmutableList<ExploreKind> = persistentListOf(),
|
||||
val kindDisplayNames: ImmutableMap<String, String> = persistentMapOf(),
|
||||
val kindValues: ImmutableMap<String, String> = persistentMapOf(),
|
||||
val loadingKinds: Boolean = false,
|
||||
val listItems: ImmutableList<ExploreListItem> = persistentListOf()
|
||||
val loadingKinds: Boolean = false
|
||||
) : ListUiState<BookSourcePart>
|
||||
|
||||
private fun buildExploreListItems(state: ExploreUiState): ImmutableList<ExploreListItem> {
|
||||
fun buildExploreListItems(state: ExploreUiState): ImmutableList<ExploreListItem> {
|
||||
if (state.items.isEmpty()) return persistentListOf()
|
||||
val expandedId = state.expandedId
|
||||
val kindRows = if (expandedId != null) {
|
||||
|
||||
@@ -37,7 +37,7 @@ import io.legado.app.ui.main.homepage.manage.SetListPage
|
||||
import io.legado.app.ui.main.homepage.manage.SourceBrowseDetailPage
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
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
|
||||
@@ -127,38 +127,38 @@ fun <T> HomepageModuleManageSheet(
|
||||
},
|
||||
startAction = {
|
||||
if (showCustomSetAddModules) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { showCustomSetAddModules = false },
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack
|
||||
)
|
||||
} else if (browsingSourceUrl != null || showSourceBrowser) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
if (browsingDetail) browsingDetail = false
|
||||
else if (showSourceBrowser) showSourceBrowser = false
|
||||
else browsingSourceUrl = null
|
||||
},
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack
|
||||
)
|
||||
} else if (selectingSetUrl != null) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { selectingSetUrl = null },
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack
|
||||
)
|
||||
}
|
||||
},
|
||||
endAction = {
|
||||
if (browsingDetail && browseTab == 2 && browseModuleType == "buttonGroup" && selectedKindTitles.isNotEmpty()) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { showAddButtonGroupDialog = true },
|
||||
imageVector = Icons.Default.Check
|
||||
icon = Icons.Default.Check
|
||||
)
|
||||
} else if ((showSourceBrowser || browsingSourceUrl != null) && !browsingDetail) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
Box {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { expanded = true },
|
||||
imageVector = Icons.Default.FilterList
|
||||
icon = Icons.Default.FilterList
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
expanded = expanded,
|
||||
|
||||
@@ -72,7 +72,7 @@ import io.legado.app.ui.widget.components.LoadMoreFooter
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.book.SearchBookGridItem
|
||||
import io.legado.app.ui.widget.components.book.SearchBookPreviewSheet
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
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.progressIndicator.AppCircularProgressIndicator
|
||||
@@ -82,6 +82,7 @@ import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.utils.sendToClip
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@@ -132,7 +133,7 @@ fun HomepageScreen(
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel) {
|
||||
viewModel.effects.collect { effect ->
|
||||
viewModel.effects.collectLatest { effect ->
|
||||
when (effect) {
|
||||
is HomepageEffect.NavigateToBookInfo ->
|
||||
onBookClick(
|
||||
@@ -717,9 +718,9 @@ private fun ModuleHeader(
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (onNavigate != null) {
|
||||
SmallTonalIconButton(
|
||||
SmallTonalButton(
|
||||
onClick = onNavigate,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowForward
|
||||
icon = Icons.AutoMirrored.Filled.ArrowForward
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,6 +338,12 @@ class HomepageViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
loadJobs.values.forEach { it.cancel() }
|
||||
loadJobs.clear()
|
||||
}
|
||||
|
||||
private suspend fun syncModulesFromSource(source: BookSource) {
|
||||
val json = source.homepageModules ?: return
|
||||
ensureSetForSource(source.bookSourceUrl, source.bookSourceName)
|
||||
|
||||
@@ -26,7 +26,7 @@ import io.legado.app.ui.main.homepage.HomepageModuleManageUi
|
||||
import io.legado.app.ui.main.homepage.HomepageViewModel
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SecondaryButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||
@@ -129,13 +129,13 @@ fun SetDetailPage(
|
||||
}
|
||||
},
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onEditModule(module) },
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onRequestDeleteModule(module.id) },
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -160,13 +160,13 @@ fun SetDetailPage(
|
||||
containerColor = if (isEffective) LegadoTheme.colorScheme.surfaceContainerHigh else LegadoTheme.colorScheme.onSheetContent,
|
||||
onEnabledChange = { onToggleModule(module.id, it) },
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onEditModule(module) },
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onRequestDeleteModule(module.id) },
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ import io.legado.app.R
|
||||
import io.legado.app.ui.main.homepage.HomepageSourceManageUi
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SecondaryButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||
import io.legado.app.utils.move
|
||||
@@ -85,13 +85,13 @@ fun SetListPage(
|
||||
}
|
||||
},
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onRenameSet(set.sourceUrl) },
|
||||
imageVector = Icons.Default.DriveFileRenameOutline
|
||||
icon = Icons.Default.DriveFileRenameOutline
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onDeleteSet(set.sourceUrl) },
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ import io.legado.app.ui.main.homepage.HomepageModuleManageUi
|
||||
import io.legado.app.ui.main.homepage.HomepageViewModel
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SecondaryButton
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||
@@ -170,13 +170,13 @@ fun SourceBrowseDetailPage(
|
||||
}
|
||||
},
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onEditModule(module) },
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onRequestDeleteModule(module.id) },
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -212,13 +212,13 @@ fun SourceBrowseDetailPage(
|
||||
containerColor = if (isEffective) LegadoTheme.colorScheme.surfaceContainerHigh else LegadoTheme.colorScheme.onSheetContent,
|
||||
onEnabledChange = { onToggleModule(module.id, it) },
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onEditModule(module) },
|
||||
imageVector = Icons.Default.Edit
|
||||
icon = Icons.Default.Edit
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onRequestDeleteModule(module.id) },
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -334,9 +334,9 @@ fun SourceBrowseDetailPage(
|
||||
onToggleSelection = { showKindSelect = true },
|
||||
trailingAction = {
|
||||
if (isButtonGroup && selectedKindTitles.isNotEmpty()) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onShowAddButtonGroupDialog() },
|
||||
imageVector = Icons.Default.Check
|
||||
icon = Icons.Default.Check
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ import androidx.compose.material.icons.filled.Source
|
||||
import androidx.compose.material.icons.filled.Web
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -54,11 +52,12 @@ import io.legado.app.ui.replace.ReplaceRuleActivity
|
||||
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.SplicedColumnGroup
|
||||
import io.legado.app.ui.widget.components.button.SmallTextButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
|
||||
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
|
||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
|
||||
@@ -81,7 +80,7 @@ fun MyScreen(
|
||||
GlassMediumFlexibleTopAppBar(
|
||||
title = stringResource(R.string.my),
|
||||
actions = {
|
||||
IconButton(
|
||||
TopBarActionButton(
|
||||
onClick = {
|
||||
onNavigate(
|
||||
PrefClickEvent.ShowMd(
|
||||
@@ -89,10 +88,10 @@ fun MyScreen(
|
||||
path = "appHelp"
|
||||
)
|
||||
)
|
||||
}
|
||||
) {Icon(
|
||||
Icons.AutoMirrored.Filled.HelpOutline, null)
|
||||
}
|
||||
},
|
||||
imageVector = Icons.AutoMirrored.Filled.HelpOutline,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
@@ -251,22 +250,22 @@ fun WebServiceSettingBlock(
|
||||
.padding(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 12.dp),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
SmallTextButton(
|
||||
text = stringResource(R.string.copy_url),
|
||||
imageVector = Icons.Default.ContentCopy,
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
onNavigate(PrefClickEvent.CopyUrl(uiState.webServiceAddress))
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.ContentCopy,
|
||||
text = stringResource(R.string.copy_url)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
SmallTextButton(
|
||||
text = stringResource(R.string.open_in_browser),
|
||||
imageVector = Icons.Default.OpenInBrowser,
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
onNavigate(PrefClickEvent.OpenUrl(uiState.webServiceAddress))
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.OpenInBrowser,
|
||||
text = stringResource(R.string.open_in_browser)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -19,71 +17,54 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.AssistChip
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.animateFloatingActionButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
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.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.ToggleChip
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
||||
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.topbar.GlassMediumFlexibleTopAppBar
|
||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@Composable
|
||||
@@ -369,9 +350,9 @@ fun GroupSelector(
|
||||
}
|
||||
}
|
||||
}
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = onManageClick,
|
||||
imageVector = Icons.Default.Settings
|
||||
icon = Icons.Default.Settings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.legado.app.ui.rss.article
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -11,13 +10,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
|
||||
import androidx.compose.material.icons.automirrored.filled.Login
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
@@ -25,7 +22,6 @@ import androidx.compose.material.icons.filled.CleaningServices
|
||||
import androidx.compose.material.icons.filled.Dataset
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.History
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Style
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -39,6 +35,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -51,10 +48,9 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallToggleButton
|
||||
import io.legado.app.ui.widget.components.button.series.ToggleStyle
|
||||
import io.legado.app.ui.widget.components.card.GlassCard
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
import io.legado.app.ui.widget.components.menuItem.MenuItemIcon
|
||||
@@ -65,6 +61,8 @@ import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
||||
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.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -242,10 +240,11 @@ fun RssSortScreen(
|
||||
|
||||
if (BookshelfConfig.shouldShowExpandButton) {
|
||||
Box {
|
||||
SmallOutlinedIconToggleButton(
|
||||
SmallToggleButton(
|
||||
checked = showGroupMenu,
|
||||
onCheckedChange = { showGroupMenu = it },
|
||||
imageVector = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||
style = ToggleStyle.Outlined,
|
||||
icon = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||
contentDescription = stringResource(R.string.group_manage)
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
@@ -321,9 +320,9 @@ private fun RssReadRecordSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.read_record),
|
||||
endAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onClear,
|
||||
imageVector = Icons.Default.CleaningServices,
|
||||
icon = Icons.Default.CleaningServices,
|
||||
contentDescription = stringResource(R.string.clear)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import io.legado.app.data.entities.RssStar
|
||||
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.ActionItem
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||
import io.legado.app.ui.widget.components.dialog.TextListInputDialog
|
||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||
@@ -229,9 +229,9 @@ fun RssFavoritesScreen(
|
||||
val openAction = {
|
||||
onOpenRead(rssStar.title, rssStar.origin, rssStar.link, null)
|
||||
}
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = openAction,
|
||||
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||
icon = Icons.AutoMirrored.Filled.OpenInNew,
|
||||
contentDescription = "Open"
|
||||
)
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ import io.legado.app.ui.login.SourceLoginActivity
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.menuItem.MenuItemIcon
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
@@ -422,9 +422,9 @@ private fun FavoriteEditSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.favorite),
|
||||
endAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onDelete,
|
||||
imageVector = Icons.Default.CleaningServices,
|
||||
icon = Icons.Default.CleaningServices,
|
||||
contentDescription = stringResource(R.string.delete)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
@@ -13,13 +12,10 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -45,7 +41,7 @@ import io.legado.app.ui.widget.components.ActionItem
|
||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||
import io.legado.app.ui.widget.components.dialog.TextListInputDialog
|
||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||
@@ -364,9 +360,9 @@ fun RssSourceScreen(
|
||||
onEnabledChange = { enabled -> viewModel.update(item.source.copy(enabled = enabled)) },
|
||||
onClickEdit = { onEditSource(item.source) },
|
||||
trailingAction = {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { showDeleteRuleDialog = item.source },
|
||||
imageVector = Icons.Default.Delete
|
||||
icon = Icons.Default.Delete
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -17,7 +17,6 @@ import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.animateFloatingActionButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -46,6 +45,7 @@ import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||
import io.legado.app.ui.widget.components.checkBox.CheckboxGroupContainer
|
||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
||||
@@ -165,9 +165,11 @@ fun RuleSubScreen(
|
||||
}
|
||||
},
|
||||
trailingAction = {
|
||||
IconButton(onClick = { showEditDialog = ruleSub }) {
|
||||
Icon(Icons.Default.Edit, contentDescription = "Edit")
|
||||
}
|
||||
SmallPlainButton(
|
||||
onClick = { showEditDialog = ruleSub },
|
||||
icon = Icons.Default.Edit,
|
||||
contentDescription = "Edit"
|
||||
)
|
||||
},
|
||||
dropdownContent = { dismiss ->
|
||||
RoundDropdownMenuItem(
|
||||
|
||||
@@ -35,6 +35,14 @@ fun Modifier.adaptiveVerticalPadding(): Modifier {
|
||||
return this.padding(horizontal = horizontal)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun adaptiveHorizonalPadding(): PaddingValues {
|
||||
val horizontal = if (ThemeResolver.isMiuixEngine(composeEngine)) 12.dp else 16.dp
|
||||
return PaddingValues(
|
||||
horizontal = horizontal
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun adaptiveContentPaddingOnlyVertical(
|
||||
top: Dp,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.legado.app.ui.widget.components
|
||||
|
||||
import androidx.compose.material3.Slider
|
||||
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.Slider as MiuixSlider
|
||||
|
||||
@Composable
|
||||
fun AppSlider(
|
||||
value: Float,
|
||||
onValueChange: (Float) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
|
||||
steps: Int = 0,
|
||||
onValueChangeFinished: (() -> Unit)? = null,
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixSlider(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
valueRange = valueRange,
|
||||
steps = steps,
|
||||
onValueChangeFinished = onValueChangeFinished
|
||||
)
|
||||
} else {
|
||||
Slider(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
valueRange = valueRange,
|
||||
steps = steps,
|
||||
onValueChangeFinished = onValueChangeFinished
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import io.legado.app.ui.widget.components.progressIndicator.AppContainedLoadingIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -24,8 +23,9 @@ import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.series.SmallTonalButton
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
import io.legado.app.ui.widget.components.button.SmallTonalTextButton
|
||||
import io.legado.app.ui.widget.components.progressIndicator.AppContainedLoadingIndicator
|
||||
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
||||
|
||||
@Composable
|
||||
@@ -84,10 +84,10 @@ fun EmptyMessage(
|
||||
|
||||
if (buttonText != null && onButtonClick != null) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
SmallTonalTextButton(
|
||||
SmallTonalButton(
|
||||
onClick = onButtonClick,
|
||||
text = buttonText,
|
||||
imageVector = buttonImageVector
|
||||
icon = buttonImageVector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.components.button.SmallTextButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.ui.widget.components.settingItem.SettingItem
|
||||
|
||||
@@ -118,13 +118,13 @@ private fun GroupItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
SmallTextButton(
|
||||
text = stringResource(id = R.string.ok),
|
||||
imageVector = Icons.Default.Check,
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
onUpdateGroup(group, state.text.toString())
|
||||
expanded = false
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.Check,
|
||||
text = stringResource(id = R.string.ok)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import androidx.compose.ui.unit.dp
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonParser
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.utils.GSON
|
||||
|
||||
@@ -37,17 +37,17 @@ fun JsonRawEditor(
|
||||
style = LegadoTheme.typography.labelMediumEmphasized
|
||||
)
|
||||
Row {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
runCatching {
|
||||
val jsonElement = JsonParser.parseString(value)
|
||||
onValueChange(GSON.toJson(jsonElement))
|
||||
}
|
||||
},
|
||||
imageVector = Icons.Default.AutoFixHigh,
|
||||
icon = Icons.Default.AutoFixHigh,
|
||||
contentDescription = "格式化"
|
||||
)
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = {
|
||||
runCatching {
|
||||
val jsonElement = JsonParser.parseString(value)
|
||||
@@ -55,7 +55,7 @@ fun JsonRawEditor(
|
||||
onValueChange(compactGson.toJson(jsonElement))
|
||||
}
|
||||
},
|
||||
imageVector = Icons.Default.Compress,
|
||||
icon = Icons.Default.Compress,
|
||||
contentDescription = "压缩"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallOutlinedButton
|
||||
import io.legado.app.ui.widget.components.card.TextCard
|
||||
|
||||
@Composable
|
||||
@@ -26,12 +26,12 @@ fun ValueStepper(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
SmallOutlinedIconButton(
|
||||
SmallOutlinedButton(
|
||||
onClick = {
|
||||
val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
|
||||
onValueChange(newValue)
|
||||
},
|
||||
imageVector = Icons.Default.Remove
|
||||
icon = Icons.Default.Remove
|
||||
)
|
||||
TextCard(
|
||||
cornerRadius = 8.dp,
|
||||
@@ -41,12 +41,12 @@ fun ValueStepper(
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
contentColor = LegadoTheme.colorScheme.onSurface
|
||||
)
|
||||
SmallOutlinedIconButton(
|
||||
SmallOutlinedButton(
|
||||
onClick = {
|
||||
val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
|
||||
onValueChange(newValue)
|
||||
},
|
||||
imageVector = Icons.Default.Add
|
||||
icon = Icons.Default.Add
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.material3.TextButton
|
||||
import io.legado.app.ui.widget.components.progressIndicator.AppContainedLoadingIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
|
||||
@Composable
|
||||
fun AnimatedTextButton(
|
||||
isLoading: Boolean,
|
||||
onClick: () -> Unit,
|
||||
text: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onClick,
|
||||
enabled = !isLoading,
|
||||
modifier = modifier.animateContentSize()
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = isLoading,
|
||||
contentAlignment = Alignment.Center,
|
||||
transitionSpec = {
|
||||
fadeIn(tween(200)) togetherWith fadeOut(tween(200))
|
||||
},
|
||||
label = "ButtonLoading"
|
||||
) { loading ->
|
||||
if (loading) {
|
||||
AppContainedLoadingIndicator()
|
||||
} else {
|
||||
AppText(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedIconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TonalToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@Composable
|
||||
fun MediumIconButton(
|
||||
onClick: () -> Unit,
|
||||
imageVector: ImageVector,
|
||||
tint: Color = LegadoTheme.colorScheme.onSurface,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = tint
|
||||
)
|
||||
}
|
||||
} else {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = tint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MediumOutlinedIconButton(
|
||||
onClick: () -> Unit,
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainerHigh
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
}
|
||||
} else {
|
||||
OutlinedIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = LegadoTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun MediumAnimatedActionButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
iconChecked: ImageVector,
|
||||
iconUnchecked: ImageVector,
|
||||
activeText: String,
|
||||
inactiveText: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.primaryContainer else MiuixTheme.colorScheme.surfaceContainerHigh,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixActionButtonContainer"
|
||||
)
|
||||
|
||||
val contentColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.onPrimaryContainer else MiuixTheme.colorScheme.onSurface,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixActionButtonContent"
|
||||
)
|
||||
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked,
|
||||
iconUnchecked = iconUnchecked,
|
||||
activeText = activeText,
|
||||
inactiveText = inactiveText,
|
||||
modifier = modifier,
|
||||
iconSize = 24.dp,
|
||||
textStyle = LegadoTheme.typography.labelMedium,
|
||||
textStartPadding = 8.dp,
|
||||
contentColor = contentColor,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
MiuixIconButton(
|
||||
onClick = { onToggle(!checked) },
|
||||
modifier = buttonModifier,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, tint ->
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier,
|
||||
tint = tint ?: Color.Unspecified
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, style, color ->
|
||||
MiuixText(
|
||||
text = label,
|
||||
color = color ?: Color.Unspecified,
|
||||
style = style,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked,
|
||||
iconUnchecked = iconUnchecked,
|
||||
activeText = activeText,
|
||||
inactiveText = inactiveText,
|
||||
modifier = modifier,
|
||||
iconSize = 24.dp,
|
||||
textStyle = LegadoTheme.typography.labelMedium,
|
||||
textStartPadding = 8.dp,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
TonalToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onToggle,
|
||||
modifier = buttonModifier,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, _ ->
|
||||
AnimatedIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, style, color ->
|
||||
Text(
|
||||
text = label,
|
||||
modifier = textModifier,
|
||||
style = style,
|
||||
color = color ?: Color.Unspecified,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@Composable
|
||||
fun MediumOutlinedButton(
|
||||
onClick: () -> Unit,
|
||||
imageVector: ImageVector,
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainerHigh
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
MiuixText(text = text)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.IconToggleButtonShapes
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
|
||||
import androidx.compose.material3.OutlinedIconButton
|
||||
import androidx.compose.material3.OutlinedIconToggleButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TonalToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
private val SmallMiuixButtonSize = 32.dp
|
||||
private val SmallMiuixIconSize = 18.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
private fun smallContainerSize() = IconButtonDefaults.extraSmallContainerSize(
|
||||
IconButtonDefaults.IconButtonWidthOption.Uniform
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
private val smallIconSize: androidx.compose.ui.unit.Dp
|
||||
get() = IconButtonDefaults.extraSmallIconSize
|
||||
|
||||
@Composable
|
||||
private fun SmallNoMinTouchTarget(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallIconButton(
|
||||
onClick: () -> Unit,
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shape = IconButtonDefaults.extraSmallRoundShape,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallOutlinedIconButton(
|
||||
onClick: () -> Unit,
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
OutlinedIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallTonalIconButton(
|
||||
onClick: () -> Unit,
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
FilledTonalIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallOutlinedIconToggleButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) LegadoTheme.colorScheme.primaryContainer else LegadoTheme.colorScheme.surfaceContainer,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixToggleContainerColor"
|
||||
)
|
||||
|
||||
val iconTint by animateColorAsState(
|
||||
targetValue = if (checked) LegadoTheme.colorScheme.onPrimaryContainer else LegadoTheme.colorScheme.onSurfaceVariant,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixToggleIconTint"
|
||||
)
|
||||
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val defaultShape = IconButtonDefaults.extraSmallRoundShape
|
||||
val pressedShape = IconButtonDefaults.extraSmallPressedShape
|
||||
val checkedShape = IconButtonDefaults.extraSmallSelectedRoundShape
|
||||
|
||||
val toggleShapes = remember(defaultShape, checkedShape) {
|
||||
IconToggleButtonShapes(
|
||||
shape = defaultShape,
|
||||
pressedShape = pressedShape,
|
||||
checkedShape = checkedShape
|
||||
)
|
||||
}
|
||||
|
||||
SmallNoMinTouchTarget {
|
||||
OutlinedIconToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shapes = toggleShapes
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallAnimatedActionButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
iconChecked: ImageVector,
|
||||
iconUnchecked: ImageVector,
|
||||
activeText: String,
|
||||
inactiveText: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.primaryContainer else MiuixTheme.colorScheme.surfaceContainer,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixActionButtonContainer"
|
||||
)
|
||||
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked,
|
||||
iconUnchecked = iconUnchecked,
|
||||
activeText = activeText,
|
||||
inactiveText = inactiveText,
|
||||
modifier = modifier,
|
||||
iconSize = 18.dp,
|
||||
textStyle = LegadoTheme.typography.labelSmall,
|
||||
textStartPadding = 6.dp,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
MiuixIconButton(
|
||||
onClick = { onToggle(!checked) },
|
||||
modifier = buttonModifier,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, tint ->
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier,
|
||||
tint = tint ?: Color.Unspecified
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, style, color ->
|
||||
MiuixText(
|
||||
text = label,
|
||||
style = style,
|
||||
color = color ?: Color.Unspecified,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked,
|
||||
iconUnchecked = iconUnchecked,
|
||||
activeText = activeText,
|
||||
inactiveText = inactiveText,
|
||||
modifier = modifier.height(36.dp),
|
||||
iconSize = 16.dp,
|
||||
textStyle = LegadoTheme.typography.labelSmall,
|
||||
textStartPadding = 6.dp,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
TonalToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onToggle,
|
||||
modifier = buttonModifier,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, _ ->
|
||||
AnimatedIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, style, color ->
|
||||
Text(
|
||||
text = label,
|
||||
style = style,
|
||||
color = color ?: Color.Unspecified,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
|
||||
@Composable
|
||||
fun SmallTextButton(
|
||||
text: String,
|
||||
imageVector: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceVariant,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
AppText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TextButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
||||
shape = MaterialTheme.shapes.small
|
||||
) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
AppText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SmallTonalTextButton(
|
||||
text: String? = null,
|
||||
imageVector: ImageVector? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceContainer,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (imageVector != null) {
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
if (text != null) {
|
||||
AppText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FilledTonalButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp)
|
||||
) {
|
||||
if (imageVector != null) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(4.dp))
|
||||
if (text != null) {
|
||||
AppText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.legado.app.ui.widget.components.button
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TonalToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun MediumAnimatedButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
iconChecked: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.primaryContainer else MiuixTheme.colorScheme.surfaceContainer,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixAnimatedContainerColor"
|
||||
)
|
||||
|
||||
val contentColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.onPrimaryContainer else MiuixTheme.colorScheme.onSurface,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixAnimatedContentColor"
|
||||
)
|
||||
|
||||
if (text != null) {
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked ?: icon!!,
|
||||
iconUnchecked = icon ?: iconChecked!!,
|
||||
activeText = text,
|
||||
inactiveText = text,
|
||||
modifier = modifier,
|
||||
iconSize = 24.dp,
|
||||
textStyle = LegadoTheme.typography.labelMedium,
|
||||
textStartPadding = 8.dp,
|
||||
contentColor = contentColor,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
MiuixIconButton(
|
||||
onClick = { onToggle(!checked) },
|
||||
modifier = buttonModifier,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, tint ->
|
||||
MiuixIcon(
|
||||
tint = tint ?: Color.Unspecified,
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, textStyle, color ->
|
||||
MiuixText(
|
||||
text = label,
|
||||
color = color ?: Color.Unspecified,
|
||||
style = textStyle,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
tint = contentColor
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (text != null) {
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked ?: icon!!,
|
||||
iconUnchecked = icon ?: iconChecked!!,
|
||||
activeText = text,
|
||||
inactiveText = text,
|
||||
modifier = modifier.height(36.dp),
|
||||
iconSize = 20.dp,
|
||||
textStyle = LegadoTheme.typography.labelMedium,
|
||||
textStartPadding = 8.dp,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
TonalToggleButton(
|
||||
modifier = buttonModifier,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp),
|
||||
checked = checked,
|
||||
onCheckedChange = onToggle
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, _ ->
|
||||
AnimatedIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, textStyle, color ->
|
||||
Text(
|
||||
text = label,
|
||||
style = textStyle,
|
||||
color = color ?: Color.Unspecified,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
FilledTonalIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedIconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
|
||||
@Composable
|
||||
fun MediumOutlinedButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
if (icon != null && text == null) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainerHigh
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceContainerHigh,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
MediumButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (icon != null && text == null) {
|
||||
OutlinedIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = LegadoTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
} else {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
MediumButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@Composable
|
||||
internal fun MediumButtonContent(
|
||||
icon: ImageVector?,
|
||||
text: String?,
|
||||
contentDescription: String?
|
||||
) {
|
||||
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (icon != null) {
|
||||
if (isMiuix) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
}
|
||||
}
|
||||
if (text != null) {
|
||||
if (isMiuix) {
|
||||
MiuixText(text = text)
|
||||
} else {
|
||||
AppText(text = text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MediumPlainButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
text: String? = null,
|
||||
tint: androidx.compose.ui.graphics.Color = LegadoTheme.colorScheme.onSurface,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
if (icon != null && text == null) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = tint
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceVariant,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
MediumButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (icon != null && text == null) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = tint
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceVariant,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
MediumButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun MediumToggleButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
style: ToggleStyle = ToggleStyle.Outlined,
|
||||
icon: ImageVector? = null,
|
||||
iconChecked: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.primaryContainer else MiuixTheme.colorScheme.surfaceContainerHigh,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixToggleContainerColor"
|
||||
)
|
||||
|
||||
val contentColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.onPrimaryContainer else MiuixTheme.colorScheme.onSurface,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixToggleContentColor"
|
||||
)
|
||||
|
||||
if (text != null) {
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = null,
|
||||
tint = contentColor
|
||||
)
|
||||
MiuixText(
|
||||
text = text,
|
||||
color = contentColor,
|
||||
style = LegadoTheme.typography.labelMedium,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
tint = contentColor
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (text != null) {
|
||||
ToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (style) {
|
||||
ToggleStyle.Outlined -> {
|
||||
FilledTonalButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ToggleStyle.Tonal -> {
|
||||
FilledTonalIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
|
||||
@Composable
|
||||
fun MediumTonalButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
if (icon != null && text == null) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceContainer,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
MediumButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (icon != null && text == null) {
|
||||
FilledTonalIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription
|
||||
)
|
||||
}
|
||||
} else {
|
||||
FilledTonalButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
MediumButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.TonalToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallAnimatedButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
iconChecked: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) LegadoTheme.colorScheme.primaryContainer else LegadoTheme.colorScheme.surfaceContainer,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixAnimatedContainerColor"
|
||||
)
|
||||
|
||||
val iconTint by animateColorAsState(
|
||||
targetValue = if (checked) LegadoTheme.colorScheme.onPrimaryContainer else LegadoTheme.colorScheme.onSurfaceVariant,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixAnimatedIconTint"
|
||||
)
|
||||
|
||||
if (text != null) {
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked ?: icon!!,
|
||||
iconUnchecked = icon ?: iconChecked!!,
|
||||
activeText = text,
|
||||
inactiveText = text,
|
||||
modifier = modifier,
|
||||
iconSize = 18.dp,
|
||||
textStyle = LegadoTheme.typography.labelSmall,
|
||||
textStartPadding = 6.dp,
|
||||
contentColor = iconTint,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
MiuixIconButton(
|
||||
onClick = { onToggle(!checked) },
|
||||
modifier = buttonModifier,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, tint ->
|
||||
MiuixIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier,
|
||||
tint = tint ?: Color.Unspecified
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, textStyle, color ->
|
||||
MiuixText(
|
||||
text = label,
|
||||
style = textStyle,
|
||||
color = color ?: Color.Unspecified,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
if (text != null) {
|
||||
AnimatedActionButtonCore(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
iconChecked = iconChecked ?: icon!!,
|
||||
iconUnchecked = icon ?: iconChecked!!,
|
||||
activeText = text,
|
||||
inactiveText = text,
|
||||
modifier = modifier.height(36.dp),
|
||||
iconSize = 16.dp,
|
||||
textStyle = LegadoTheme.typography.labelSmall,
|
||||
textStartPadding = 6.dp,
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
TonalToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onToggle,
|
||||
modifier = buttonModifier,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
},
|
||||
icon = { imageVector, iconModifier, _ ->
|
||||
AnimatedIcon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = iconModifier
|
||||
)
|
||||
},
|
||||
text = { label, textModifier, textStyle, color ->
|
||||
androidx.compose.material3.Text(
|
||||
text = label,
|
||||
style = textStyle,
|
||||
color = color ?: Color.Unspecified,
|
||||
modifier = textModifier,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
FilledTonalIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedIconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallOutlinedButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
if (icon != null && text == null) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceContainer,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
SmallButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
when {
|
||||
icon != null && text == null -> {
|
||||
OutlinedIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
||||
border = ButtonDefaults.outlinedButtonBorder()
|
||||
) {
|
||||
SmallButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
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.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
internal val SmallMiuixButtonSize = 32.dp
|
||||
internal val SmallMiuixIconSize = 18.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
internal fun smallContainerSize() = IconButtonDefaults.extraSmallContainerSize(
|
||||
IconButtonDefaults.IconButtonWidthOption.Uniform
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
internal val smallIconSize: Dp
|
||||
get() = IconButtonDefaults.extraSmallIconSize
|
||||
|
||||
@Composable
|
||||
internal fun SmallNoMinTouchTarget(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun SmallButtonContent(
|
||||
icon: ImageVector?,
|
||||
text: String?,
|
||||
contentDescription: String?
|
||||
) {
|
||||
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (icon != null) {
|
||||
if (isMiuix) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (text != null) {
|
||||
if (isMiuix) {
|
||||
MiuixText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
} else {
|
||||
AppText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SmallPlainButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
if (icon != null && text == null) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(SmallMiuixIconSize),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceVariant,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
SmallButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
when {
|
||||
icon != null && text == null -> {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shape = IconButtonDefaults.extraSmallRoundShape,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
TextButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
||||
shape = MaterialTheme.shapes.small
|
||||
) {
|
||||
SmallButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.IconToggleButtonShapes
|
||||
import androidx.compose.material3.OutlinedIconToggleButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TonalToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
enum class ToggleStyle { Outlined, Tonal }
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallToggleButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
style: ToggleStyle = ToggleStyle.Outlined,
|
||||
icon: ImageVector? = null,
|
||||
iconChecked: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) LegadoTheme.colorScheme.primaryContainer else LegadoTheme.colorScheme.surfaceContainer,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixToggleContainerColor"
|
||||
)
|
||||
|
||||
val iconTint by animateColorAsState(
|
||||
targetValue = if (checked) LegadoTheme.colorScheme.onPrimaryContainer else LegadoTheme.colorScheme.onSurfaceVariant,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixToggleIconTint"
|
||||
)
|
||||
|
||||
if (text != null) {
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = iconTint
|
||||
)
|
||||
MiuixText(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelSmall,
|
||||
color = iconTint,
|
||||
modifier = Modifier.padding(start = 6.dp),
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MiuixIconButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = containerColor
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
if (text != null) {
|
||||
TonalToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
style = LegadoTheme.typography.labelSmall,
|
||||
modifier = Modifier.padding(start = 6.dp),
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (style) {
|
||||
ToggleStyle.Outlined -> {
|
||||
val defaultShape = IconButtonDefaults.extraSmallRoundShape
|
||||
val pressedShape = IconButtonDefaults.extraSmallPressedShape
|
||||
val checkedShape = IconButtonDefaults.extraSmallSelectedRoundShape
|
||||
|
||||
val toggleShapes = remember(defaultShape, checkedShape) {
|
||||
IconToggleButtonShapes(
|
||||
shape = defaultShape,
|
||||
pressedShape = pressedShape,
|
||||
checkedShape = checkedShape
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedIconToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shapes = toggleShapes
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ToggleStyle.Tonal -> {
|
||||
FilledTonalButton(
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (checked) (iconChecked ?: icon)!! else icon!!,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package io.legado.app.ui.widget.components.button.series
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
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.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.CardDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SmallTonalButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
text: String? = null,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||
if (icon != null && text == null) {
|
||||
MiuixIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(SmallMiuixButtonSize),
|
||||
enabled = enabled,
|
||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
MiuixIcon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(SmallMiuixIconSize)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
showIndication = true,
|
||||
colors = CardDefaults.defaultColors(
|
||||
color = LegadoTheme.colorScheme.surfaceContainer,
|
||||
contentColor = LegadoTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
SmallButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SmallNoMinTouchTarget {
|
||||
when {
|
||||
icon != null && text == null -> {
|
||||
FilledTonalIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(smallContainerSize()),
|
||||
enabled = enabled,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.size(smallIconSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
FilledTonalButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp)
|
||||
) {
|
||||
SmallButtonContent(icon, text, contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,8 @@ import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.Checkbox
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -42,7 +37,7 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.widget.components.AdaptiveSwitch
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
import io.legado.app.ui.widget.components.checkBox.AppCheckbox
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
@@ -50,7 +45,6 @@ import io.legado.app.ui.widget.components.text.AppText
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.ReorderableLazyListState
|
||||
import top.yukonga.miuix.kmp.basic.BasicComponent
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
|
||||
@Composable
|
||||
fun SelectionItemCard(
|
||||
@@ -218,9 +212,9 @@ fun SelectionItemCardContent(
|
||||
}
|
||||
|
||||
if (onClickEdit != null) {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onClickEdit,
|
||||
imageVector = AppIcons.Edit,
|
||||
icon = AppIcons.Edit,
|
||||
contentDescription = "Edit"
|
||||
)
|
||||
}
|
||||
@@ -231,9 +225,9 @@ fun SelectionItemCardContent(
|
||||
|
||||
if (dropdownContent != null) {
|
||||
Box {
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { showMenu = true },
|
||||
imageVector = AppIcons.MoreVert,
|
||||
icon = AppIcons.MoreVert,
|
||||
contentDescription = "More"
|
||||
)
|
||||
RoundDropdownMenu(
|
||||
|
||||
+7
-8
@@ -36,8 +36,8 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.button.MediumAnimatedActionButton
|
||||
import io.legado.app.ui.widget.components.button.MediumOutlinedIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumOutlinedButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumToggleButton
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import java.time.LocalDate
|
||||
|
||||
@@ -54,15 +54,14 @@ fun HeatmapCalendarStartAction(
|
||||
currentMode: HeatmapMode,
|
||||
onModeChanged: (HeatmapMode) -> Unit,
|
||||
) {
|
||||
MediumAnimatedActionButton(
|
||||
MediumToggleButton(
|
||||
checked = currentMode == HeatmapMode.TIME,
|
||||
onCheckedChange = {
|
||||
onModeChanged(if (it) HeatmapMode.TIME else HeatmapMode.COUNT)
|
||||
},
|
||||
icon = Icons.Default.FormatListNumbered,
|
||||
iconChecked = Icons.Default.AccessTime,
|
||||
iconUnchecked = Icons.Default.FormatListNumbered,
|
||||
activeText = "按时长",
|
||||
inactiveText = "按次数"
|
||||
text = "按时长"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,9 +72,9 @@ fun HeatmapCalendarStartAction(
|
||||
fun HeatmapCalendarEndAction(
|
||||
onClearDate: () -> Unit
|
||||
) {
|
||||
MediumOutlinedIconButton(
|
||||
MediumOutlinedButton(
|
||||
onClick = onClearDate,
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
icon = Icons.Outlined.Delete,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -44,7 +44,7 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
|
||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||
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.modalBottomSheet.AppModalBottomSheet
|
||||
@@ -183,9 +183,9 @@ fun <T> BatchImportDialog(
|
||||
title = sheetTitle,
|
||||
startAction = if (isEditing) {
|
||||
{
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { editingIndex = null },
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "返回"
|
||||
)
|
||||
}
|
||||
@@ -196,9 +196,9 @@ fun <T> BatchImportDialog(
|
||||
{
|
||||
Row {
|
||||
topBarActions()
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = { onToggleAll(!allSelected) },
|
||||
imageVector = Icons.Default.SelectAll,
|
||||
icon = Icons.Default.SelectAll,
|
||||
contentDescription = if (allSelected) "全不选" else "全选"
|
||||
)
|
||||
}
|
||||
@@ -371,9 +371,9 @@ fun ImportItemRow(
|
||||
modifier = Modifier.padding(end = 4.dp)
|
||||
)
|
||||
|
||||
SmallIconButton(
|
||||
SmallPlainButton(
|
||||
onClick = onInfoClick,
|
||||
imageVector = Icons.Default.Info,
|
||||
icon = Icons.Default.Info,
|
||||
contentDescription = "详情"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.legado.app.R
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.EmptyMessage
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.utils.LogUtils
|
||||
@@ -43,12 +43,12 @@ fun AppLogSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = stringResource(R.string.log),
|
||||
endAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = {
|
||||
AppLog.clear()
|
||||
logs = emptyList()
|
||||
},
|
||||
imageVector = Icons.Default.DeleteSweep
|
||||
icon = Icons.Default.DeleteSweep
|
||||
)
|
||||
}
|
||||
) {
|
||||
|
||||
+1
-3
@@ -10,8 +10,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -65,7 +63,7 @@ fun RowScope.OptionCard(
|
||||
.weight(1f)
|
||||
.height(100.dp),
|
||||
cornerRadius = 12.dp,
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||
containerColor = LegadoTheme.colorScheme.onSheetContent,
|
||||
elevation = 2.dp
|
||||
) {
|
||||
Column(
|
||||
|
||||
@@ -13,15 +13,8 @@ import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.ContentPaste
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -30,19 +23,16 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||
import io.legado.app.ui.widget.components.AppTextField
|
||||
import io.legado.app.ui.widget.components.button.MediumIconButton
|
||||
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
|
||||
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.modalBottomSheet.AppModalBottomSheet
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
@@ -84,17 +74,17 @@ fun <T> RuleEditSheet(
|
||||
AppModalBottomSheet(
|
||||
title = title,
|
||||
startAction = {
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = onDismissRequest,
|
||||
imageVector = Icons.Default.Close,
|
||||
icon = Icons.Default.Close,
|
||||
contentDescription = "Close",
|
||||
)
|
||||
},
|
||||
endAction = {
|
||||
Box{
|
||||
MediumIconButton(
|
||||
MediumPlainButton(
|
||||
onClick = { showMenu = true },
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
icon = Icons.Default.MoreVert,
|
||||
contentDescription = "More"
|
||||
)
|
||||
RoundDropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
||||
|
||||
+25
-4
@@ -1,9 +1,13 @@
|
||||
package io.legado.app.ui.widget.components.topbar
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -16,6 +20,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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.LocalHazeState
|
||||
@@ -81,7 +86,9 @@ fun GlassMediumFlexibleTopAppBar(
|
||||
}
|
||||
|
||||
val finalModifier = if (hazeState != null) {
|
||||
modifier.background(color = animatedColor).responsiveHazeEffect(state = hazeState)
|
||||
modifier
|
||||
.background(color = animatedColor)
|
||||
.responsiveHazeEffect(state = hazeState)
|
||||
} else {
|
||||
modifier.background(color = animatedColor)
|
||||
}
|
||||
@@ -116,7 +123,7 @@ fun GlassMediumFlexibleTopAppBar(
|
||||
AdaptiveAnimatedText(
|
||||
text = title,
|
||||
useCharMode = useCharMode,
|
||||
maxLines = 1,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
@@ -126,7 +133,13 @@ fun GlassMediumFlexibleTopAppBar(
|
||||
}
|
||||
},
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
actions = {
|
||||
Box(modifier = Modifier.padding(end = 12.dp)) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) { actions() }
|
||||
}
|
||||
},
|
||||
scrollBehavior = (scrollBehavior as? M3GlassScrollBehavior)?.m3Behavior,
|
||||
colors = transparentColors
|
||||
)
|
||||
@@ -153,7 +166,15 @@ fun GlassMediumFlexibleTopAppBar(
|
||||
}
|
||||
},
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
actions = {
|
||||
Box(modifier = Modifier.padding(end = 12.dp)) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
actions()
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = (scrollBehavior as? M3GlassScrollBehavior)?.m3Behavior,
|
||||
colors = transparentColors
|
||||
)
|
||||
|
||||
@@ -30,13 +30,13 @@ import io.legado.app.R
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.ThemeResolver
|
||||
import io.legado.app.ui.widget.components.button.AnimatedActionButtonCore
|
||||
import io.legado.app.ui.widget.components.button.AnimatedIcon
|
||||
import io.legado.app.ui.widget.components.button.series.AnimatedActionButtonCore
|
||||
import io.legado.app.ui.widget.components.button.series.AnimatedIcon
|
||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
||||
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||
|
||||
@Composable
|
||||
private fun TopBarButton(
|
||||
@@ -112,7 +112,7 @@ fun TopBarActionButton(
|
||||
onClick = onClick,
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier.padding(end = 12.dp)
|
||||
modifier = modifier
|
||||
)
|
||||
} else {
|
||||
IconButton(
|
||||
@@ -140,16 +140,10 @@ fun TopBarAnimatedActionButton(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
||||
val containerColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.primaryContainer else MiuixTheme.colorScheme.surfaceContainerHigh,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixActionButtonContainer"
|
||||
)
|
||||
|
||||
val contentColor by animateColorAsState(
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.onPrimaryContainer else MiuixTheme.colorScheme.onSurface,
|
||||
targetValue = if (checked) MiuixTheme.colorScheme.primary else MiuixTheme.colorScheme.onSurface,
|
||||
animationSpec = tween(150),
|
||||
label = "MiuixActionButtonContainer"
|
||||
label = "MiuixActionButtonContent"
|
||||
)
|
||||
|
||||
AnimatedActionButtonCore(
|
||||
@@ -167,8 +161,7 @@ fun TopBarAnimatedActionButton(
|
||||
button = { buttonModifier, onToggle, content ->
|
||||
MiuixIconButton(
|
||||
onClick = { onToggle(!checked) },
|
||||
modifier = buttonModifier,
|
||||
backgroundColor = containerColor
|
||||
modifier = buttonModifier
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
package io.legado.app.web.socket
|
||||
|
||||
import io.ktor.server.websocket.*
|
||||
import io.ktor.websocket.*
|
||||
import io.ktor.server.websocket.DefaultWebSocketServerSession
|
||||
import io.ktor.websocket.CloseReason
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.close
|
||||
import io.ktor.websocket.readText
|
||||
import io.ktor.websocket.send
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.data.local.preferences.LocalPreferencesKeys
|
||||
import io.legado.app.data.local.preferences.LocalPreferencesRepository
|
||||
import io.legado.app.domain.model.BookSearchScope
|
||||
import io.legado.app.domain.model.MatchMode
|
||||
import io.legado.app.domain.usecase.BookSearchControl
|
||||
import io.legado.app.domain.usecase.BookSearchRequest
|
||||
import io.legado.app.domain.usecase.SearchBooksUseCase
|
||||
import io.legado.app.domain.usecase.SearchRunEvent
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.ui.config.otherConfig.OtherConfig
|
||||
import io.legado.app.utils.*
|
||||
import kotlinx.coroutines.*
|
||||
import io.legado.app.utils.GSON
|
||||
import io.legado.app.utils.fromJsonObject
|
||||
import io.legado.app.utils.isJson
|
||||
import io.legado.app.utils.printOnDebug
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.context.GlobalContext
|
||||
import splitties.init.appCtx
|
||||
|
||||
class BookSearchWebSocket(private val session: DefaultWebSocketServerSession) : CoroutineScope by session {
|
||||
|
||||
private val searchBooksUseCase: SearchBooksUseCase by lazy { GlobalContext.get().get() }
|
||||
private val localPreferencesRepository: LocalPreferencesRepository by lazy {
|
||||
GlobalContext.get().get()
|
||||
}
|
||||
private val searchControl = BookSearchControl()
|
||||
private val sentBookUrls = linkedSetOf<String>()
|
||||
private var searchJob: Job? = null
|
||||
@@ -66,7 +83,14 @@ class BookSearchWebSocket(private val session: DefaultWebSocketServerSession) :
|
||||
keyword = key,
|
||||
page = 1,
|
||||
scope = BookSearchScope(AppConfig.searchScope),
|
||||
precision = appCtx.getPrefBoolean(PreferKey.precisionSearch),
|
||||
matchMode = MatchMode.of(
|
||||
localPreferencesRepository
|
||||
.getPreference(
|
||||
LocalPreferencesKeys.MATCH_MODE,
|
||||
MatchMode.DEFAULT.value
|
||||
)
|
||||
.first()
|
||||
),
|
||||
concurrency = OtherConfig.threadCount,
|
||||
),
|
||||
searchControl
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<string name="dialog_setting">去设置</string>
|
||||
<string name="tip_cannot_jump_setting_page">无法跳转至设置界面</string>
|
||||
|
||||
<string name="manual_input">手动输入</string>
|
||||
<string name="manual_input">输入URL</string>
|
||||
<string name="preferred_contrast">偏好对比度</string>
|
||||
<string name="contrast_source_system_summary">来源:系统对比度</string>
|
||||
<string name="contrast_source_manual_summary">来源:手动输入</string>
|
||||
@@ -897,7 +897,7 @@
|
||||
<string name="body_title">正文标题</string>
|
||||
<string name="show_hide">显示/隐藏</string>
|
||||
<string name="header_footer">页眉与页脚</string>
|
||||
<string name="rule_subscription">规则订阅</string>
|
||||
<string name="rule_subscription">规则</string>
|
||||
<string name="rule_sub_empty_msg">添加大佬们提供的规则导入地址\n添加后点击可导入规则</string>
|
||||
<string name="get_book_progress">拉取云端进度</string>
|
||||
<string name="cover_book_progress">覆盖云端进度</string>
|
||||
@@ -1643,6 +1643,8 @@
|
||||
<string name="search_source_progress"> · 进度 %1$d/%2$d</string>
|
||||
<string name="search_empty_scope_disable_precision">%1$s分组搜索结果为空,是否关闭精准搜索?</string>
|
||||
<string name="search_empty_scope_switch_all">%1$s分组搜索结果为空,是否切换到全部分组?</string>
|
||||
<string name="search_layout_list">列表布局</string>
|
||||
<string name="search_layout_source_grouped">来源分组</string>
|
||||
<string name="download_cache_config">下载与缓存</string>
|
||||
<string name="http_cache">HTTP缓存</string>
|
||||
<string name="cover_cache">封面缓存</string>
|
||||
|
||||
@@ -1679,6 +1679,8 @@
|
||||
<string name="search_source_progress"> · Progress %1$d/%2$d</string>
|
||||
<string name="search_empty_scope_disable_precision">%1$s group search returned no results. Disable precise search?</string>
|
||||
<string name="search_empty_scope_switch_all">%1$s group search returned no results. Switch to all groups?</string>
|
||||
<string name="search_layout_list">List layout</string>
|
||||
<string name="search_layout_source_grouped">Source grouped</string>
|
||||
<string name="download_cache_config">Download & Cache</string>
|
||||
<string name="http_cache">HTTP Cache</string>
|
||||
<string name="cover_cache">Cover Cache</string>
|
||||
|
||||
Reference in New Issue
Block a user