From 30fe70ffae79a2d454fb44f018826ba4164dd3d6 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Wed, 27 May 2026 00:55:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E7=A5=9E=E5=BF=85=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../local/preferences/LocalPreferences.kt | 3 + .../app/data/repository/ExploreRepository.kt | 5 +- .../app/domain/gateway/ExploreBooksGateway.kt | 7 +- .../io/legado/app/domain/model/MatchMode.kt | 10 + .../app/domain/usecase/ExploreBooksUseCase.kt | 11 +- .../io/legado/app/model/webBook/WebBook.kt | 9 +- .../cache/manage/BookCacheManageScreen.kt | 22 +- .../ui/book/explore/ExploreShowContract.kt | 65 ++++ .../app/ui/book/explore/ExploreShowScreen.kt | 224 ++++++----- .../ui/book/explore/ExploreShowViewModel.kt | 257 +++++++------ .../app/ui/book/group/GroupEditSheet.kt | 10 +- .../ui/book/import/local/ImportBookScreen.kt | 12 +- .../ui/book/import/remote/RemoteBookScreen.kt | 26 +- .../legado/app/ui/book/info/BookInfoScreen.kt | 6 +- .../legado/app/ui/book/info/BookInfoSheets.kt | 40 +- .../ui/book/info/edit/BookInfoEditScreen.kt | 22 +- .../ui/book/manage/BookshelfManageScreen.kt | 71 ++-- .../readRecord/ReadRecordOverviewScreen.kt | 10 +- .../app/ui/book/search/ScopeSelectSheet.kt | 2 +- .../legado/app/ui/book/search/SearchScreen.kt | 2 +- .../app/ui/book/search/SearchSourceSection.kt | 111 ++++++ .../book/searchContent/SearchContentScreen.kt | 8 +- .../io/legado/app/ui/book/toc/TocScreen.kt | 8 +- .../app/ui/book/toc/rule/TxtRuleScreen.kt | 16 +- .../coverConfig/CoverRuleConfigSheet.kt | 10 +- .../DirectLinkUploadBottomSheet.kt | 15 +- .../themeConfig/BackgroundImageManageSheet.kt | 6 +- .../themeConfig/LabelColorManageSheet.kt | 28 +- .../config/themeConfig/NavIconManageSheet.kt | 6 +- .../config/themeConfig/ThemeConfigScreen.kt | 14 +- .../ui/config/themeManage/EditThemeSheet.kt | 6 +- .../config/themeManage/ThemeManageScreen.kt | 14 +- .../legado/app/ui/dict/rule/DictRuleScreen.kt | 6 +- .../io/legado/app/ui/main/MainActivity.kt | 2 +- .../java/io/legado/app/ui/main/MainIntent.kt | 2 +- .../io/legado/app/ui/main/MainNavGraph.kt | 15 +- .../io/legado/app/ui/main/MainNavigator.kt | 2 +- .../app/ui/main/bookshelf/BookshelfScreen.kt | 16 +- .../ui/main/bookshelf/BookshelfViewModel.kt | 121 +++--- .../app/ui/main/bookshelf/GroupManageSheet.kt | 8 +- .../app/ui/main/explore/ExploreScreen.kt | 11 +- .../app/ui/main/explore/ExploreViewModel.kt | 7 +- .../homepage/HomepageModuleManageSheet.kt | 22 +- .../app/ui/main/homepage/HomepageScreen.kt | 9 +- .../app/ui/main/homepage/HomepageViewModel.kt | 6 + .../ui/main/homepage/manage/SetDetailPage.kt | 18 +- .../ui/main/homepage/manage/SetListPage.kt | 10 +- .../homepage/manage/SourceBrowseDetailPage.kt | 22 +- .../java/io/legado/app/ui/main/my/MyScreen.kt | 31 +- .../app/ui/replace/edit/ReplaceEditScreen.kt | 35 +- .../app/ui/rss/article/RssSortScreen.kt | 27 +- .../ui/rss/favorites/RssFavoritesScreen.kt | 6 +- .../app/ui/rss/read/RssReadRouteScreen.kt | 6 +- .../ui/rss/source/manage/RssSourceScreen.kt | 10 +- .../app/ui/rss/subscription/RuleSubScreen.kt | 10 +- .../io/legado/app/ui/theme/AdaptivePadding.kt | 8 + .../app/ui/widget/components/AppSlider.kt | 41 ++ .../app/ui/widget/components/EmptyMessage.kt | 8 +- .../components/GroupManageBottomSheet.kt | 10 +- .../app/ui/widget/components/JsonRawEditor.kt | 10 +- .../app/ui/widget/components/ValueStepper.kt | 10 +- .../components/button/AnimatedTextButton.kt | 43 --- .../components/button/MediumIconButton.kt | 217 ----------- .../components/button/MediumTextButton.kt | 66 ---- .../components/button/SmallIconButton.kt | 356 ------------------ .../components/button/SmallTextButton.kt | 142 ------- .../{ => series}/AnimatedActionButtonCore.kt | 2 +- .../button/{ => series}/AnimatedIcon.kt | 2 +- .../button/series/MediumAnimatedButton.kt | 173 +++++++++ .../button/series/MediumOutlinedButton.kt | 78 ++++ .../button/series/MediumPlainButton.kt | 121 ++++++ .../button/series/MediumToggleButton.kt | 152 ++++++++ .../button/series/MediumTonalButton.kt | 74 ++++ .../button/series/SmallAnimatedButton.kt | 176 +++++++++ .../button/series/SmallOutlinedButton.kt | 93 +++++ .../button/series/SmallPlainButton.kt | 161 ++++++++ .../button/series/SmallToggleButton.kt | 178 +++++++++ .../button/series/SmallTonalButton.kt | 91 +++++ .../components/card/SelectionItemCard.kt | 16 +- .../heatmap/HeatmapCalendarComponents.kt | 15 +- .../importComponents/ImportComponents.kt | 14 +- .../ui/widget/components/log/AppLogSheet.kt | 6 +- .../modalBottomSheet/OptionSheet.kt | 4 +- .../widget/components/rules/RuleEditSheet.kt | 20 +- .../topbar/GlassMediumFlexibleTopAppBar.kt | 29 +- .../widget/components/topbar/TopBarButton.kt | 21 +- .../app/web/socket/BookSearchWebSocket.kt | 36 +- app/src/main/res/values-zh-rCN/strings.xml | 6 +- app/src/main/res/values/strings.xml | 2 + 89 files changed, 2278 insertions(+), 1561 deletions(-) create mode 100644 app/src/main/java/io/legado/app/domain/model/MatchMode.kt create mode 100644 app/src/main/java/io/legado/app/ui/book/explore/ExploreShowContract.kt create mode 100644 app/src/main/java/io/legado/app/ui/book/search/SearchSourceSection.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/AppSlider.kt delete mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedTextButton.kt delete mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/MediumIconButton.kt delete mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/MediumTextButton.kt delete mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/SmallIconButton.kt delete mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/SmallTextButton.kt rename app/src/main/java/io/legado/app/ui/widget/components/button/{ => series}/AnimatedActionButtonCore.kt (97%) rename app/src/main/java/io/legado/app/ui/widget/components/button/{ => series}/AnimatedIcon.kt (94%) create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumAnimatedButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumOutlinedButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumPlainButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumToggleButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumTonalButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallAnimatedButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallOutlinedButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallPlainButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallToggleButton.kt create mode 100644 app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallTonalButton.kt diff --git a/app/src/main/java/io/legado/app/data/local/preferences/LocalPreferences.kt b/app/src/main/java/io/legado/app/data/local/preferences/LocalPreferences.kt index 669b335fa..1fd62d84e 100644 --- a/app/src/main/java/io/legado/app/data/local/preferences/LocalPreferences.kt +++ b/app/src/main/java/io/legado/app/data/local/preferences/LocalPreferences.kt @@ -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 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") } diff --git a/app/src/main/java/io/legado/app/data/repository/ExploreRepository.kt b/app/src/main/java/io/legado/app/data/repository/ExploreRepository.kt index 338b5592a..863d00ff5 100644 --- a/app/src/main/java/io/legado/app/data/repository/ExploreRepository.kt +++ b/app/src/main/java/io/legado/app/data/repository/ExploreRepository.kt @@ -80,9 +80,10 @@ class ExploreRepositoryImpl( override suspend fun exploreBooks( bookSource: BookSource, url: String, - page: Int + page: Int, + key: String? ): List { - return WebBook.exploreBookSuspend(bookSource, url, page) + return WebBook.exploreBookSuspend(bookSource, url, page, key = key, isSearch = key != null) } override suspend fun getSourceExploreKinds(sourceUrl: String): List = withContext(IO) { diff --git a/app/src/main/java/io/legado/app/domain/gateway/ExploreBooksGateway.kt b/app/src/main/java/io/legado/app/domain/gateway/ExploreBooksGateway.kt index f549c0a59..d1229e15e 100644 --- a/app/src/main/java/io/legado/app/domain/gateway/ExploreBooksGateway.kt +++ b/app/src/main/java/io/legado/app/domain/gateway/ExploreBooksGateway.kt @@ -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 + suspend fun exploreBooks( + bookSource: BookSource, + url: String, + page: Int, + key: String? = null + ): List } diff --git a/app/src/main/java/io/legado/app/domain/model/MatchMode.kt b/app/src/main/java/io/legado/app/domain/model/MatchMode.kt new file mode 100644 index 000000000..852a43419 --- /dev/null +++ b/app/src/main/java/io/legado/app/domain/model/MatchMode.kt @@ -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 } + } +} diff --git a/app/src/main/java/io/legado/app/domain/usecase/ExploreBooksUseCase.kt b/app/src/main/java/io/legado/app/domain/usecase/ExploreBooksUseCase.kt index d43034502..b9f7b1a2f 100644 --- a/app/src/main/java/io/legado/app/domain/usecase/ExploreBooksUseCase.kt +++ b/app/src/main/java/io/legado/app/domain/usecase/ExploreBooksUseCase.kt @@ -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) } diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 9240f3b41..6b9346658 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -109,9 +109,12 @@ object WebBook { url: String, page: Int? = 1, ruleData: RuleDataInterface = RuleData(), + key: String? = null, + isSearch: Boolean = false, ): ArrayList { 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 { - return exploreBookAwait(bookSource, url, page, ruleData) + return exploreBookAwait(bookSource, url, page, ruleData, key, isSearch) } suspend fun exploreBookWithResolvedUrl( diff --git a/app/src/main/java/io/legado/app/ui/book/cache/manage/BookCacheManageScreen.kt b/app/src/main/java/io/legado/app/ui/book/cache/manage/BookCacheManageScreen.kt index 0b7fbfe19..815c0a5ea 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/manage/BookCacheManageScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/manage/BookCacheManageScreen.kt @@ -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 ) } diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowContract.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowContract.kt new file mode 100644 index 000000000..6791a83b8 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowContract.kt @@ -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 = persistentListOf(), + val kinds: ImmutableList = 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 +} diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt index 4ebfb255f..89cadf4ac 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt @@ -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(null) } var previewSharedCoverKey by remember { mutableStateOf(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 ) } - diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt index cc0da0334..44b0d9b61 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt @@ -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 { - data object Loading : UiState() - data class Success(val data: T) : UiState() - data class Error(val message: String) : UiState() - data object Empty : UiState() -} - -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>(emptyList()) + private val _bookshelf = MutableStateFlow>(emptySet()) private val _isLoading = MutableStateFlow(false) - private val _isRefreshing = MutableStateFlow(false) + private val _isEnd = MutableStateFlow(false) private val _errorMsg = MutableStateFlow(null) + private val _kinds = MutableStateFlow>(emptyList()) + private val _selectedKindTitle = MutableStateFlow(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>(emptySet()) - private val _kinds = MutableStateFlow>(emptyList()) - val kinds = _kinds.asStateFlow() - private val _selectedKindTitle = MutableStateFlow(null) - val selectedKindTitle = _selectedKindTitle.asStateFlow() - private val _layoutState = MutableStateFlow(AppConfig.exploreLayoutState) // 0=列表, 1=网格 - val layoutState: StateFlow = _layoutState.asStateFlow() - private val _gridCount = MutableStateFlow(appCtx.exploreLayoutGrid) - val gridCount = _gridCount.asStateFlow() - val isEnd: StateFlow = _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(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 + val bookshelf = values[1] as Set + val isLoading = values[2] as Boolean + val isEnd = values[3] as Boolean + val errorMsg = values[4] as String? + val kinds = values[5] as List + 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) } } diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt index 58a249b21..04c0cbb4d 100644 --- a/app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt +++ b/app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt @@ -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 ) } diff --git a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookScreen.kt b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookScreen.kt index 80488b2d9..fb19f67db 100644 --- a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookScreen.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt b/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt index d84aeff04..c068a5f92 100644 --- a/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt @@ -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, diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt index 660845acb..f3159140d 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt @@ -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", ) } diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt index c4a64876b..714a2b870 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt @@ -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 ) diff --git a/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt b/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt index a1f1fb06f..e7ba1a3eb 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt @@ -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 ) } diff --git a/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt b/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt index e4b7e3dba..0b28de08e 100644 --- a/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt @@ -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, ) }, diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt index 3e81955ee..d56385ae4 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt b/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt index 5ce44fe51..8da45444b 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/ScopeSelectSheet.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt index 242b62b56..f3c6bb72d 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchSourceSection.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchSourceSection.kt new file mode 100644 index 000000000..1e13f28dc --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchSourceSection.kt @@ -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, + 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, + ) + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt index f664e4f04..f3672607b 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt index 77183c93c..3d299907f 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt @@ -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( diff --git a/app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt b/app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt index 797a8aa54..8d25b810c 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt @@ -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 ) } ) diff --git a/app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt b/app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt index e85cee18e..22f7de726 100644 --- a/app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt @@ -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) diff --git a/app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt b/app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt index 8f8729c47..9f26076a8 100644 --- a/app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt @@ -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, diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt index a3d6d2d79..3a724a3bb 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/LabelColorManageSheet.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/LabelColorManageSheet.kt index 15ee1121a..80ce5334c 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeConfig/LabelColorManageSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/LabelColorManageSheet.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/NavIconManageSheet.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/NavIconManageSheet.kt index 2b47ecbe3..434e1da6b 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeConfig/NavIconManageSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/NavIconManageSheet.kt @@ -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 { diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt index d04728771..4ed50ccd7 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt @@ -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) } ) diff --git a/app/src/main/java/io/legado/app/ui/config/themeManage/EditThemeSheet.kt b/app/src/main/java/io/legado/app/ui/config/themeManage/EditThemeSheet.kt index 5496cba9c..6e9cbd6f1 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeManage/EditThemeSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeManage/EditThemeSheet.kt @@ -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" ) } diff --git a/app/src/main/java/io/legado/app/ui/config/themeManage/ThemeManageScreen.kt b/app/src/main/java/io/legado/app/ui/config/themeManage/ThemeManageScreen.kt index 016928dc9..b5e0c602e 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeManage/ThemeManageScreen.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeManage/ThemeManageScreen.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt b/app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt index 50edfef22..cee923bc0 100644 --- a/app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt +++ b/app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt @@ -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 ) } ) diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index c8972f406..34165cc65 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -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) } diff --git a/app/src/main/java/io/legado/app/ui/main/MainIntent.kt b/app/src/main/java/io/legado/app/ui/main/MainIntent.kt index 0f70a3c8b..f14cabc33 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainIntent.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainIntent.kt @@ -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) diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt b/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt index 1156b1805..15b818ddf 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt @@ -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 { route -> + val exploreViewModel = koinViewModel() + + 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( diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt b/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt index fc9a82973..fe2197638 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt index c486447ac..bd4c54c54 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt @@ -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) ) } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt index 17b877585..43e20f052 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt @@ -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>> = 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>> = + 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>()) { 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>> = - 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> = 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? ) - 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, - 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>>( - 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 diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt index a25e76897..2888b82e3 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt @@ -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( diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt index 63848142d..c782d9eeb 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt @@ -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(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) diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreViewModel.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreViewModel.kt index 61b027fcb..de1ce9d59 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreViewModel.kt @@ -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 = _uiState - .map { state -> state.copy(listItems = buildExploreListItems(state)) } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ExploreUiState()) private val _effects = MutableSharedFlow(extraBufferCapacity = 8) val effects = _effects.asSharedFlow() @@ -200,11 +198,10 @@ class ExploreViewModel( val exploreKinds: ImmutableList = persistentListOf(), val kindDisplayNames: ImmutableMap = persistentMapOf(), val kindValues: ImmutableMap = persistentMapOf(), - val loadingKinds: Boolean = false, - val listItems: ImmutableList = persistentListOf() + val loadingKinds: Boolean = false ) : ListUiState - private fun buildExploreListItems(state: ExploreUiState): ImmutableList { + fun buildExploreListItems(state: ExploreUiState): ImmutableList { if (state.items.isEmpty()) return persistentListOf() val expandedId = state.expandedId val kindRows = if (expandedId != null) { diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageModuleManageSheet.kt b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageModuleManageSheet.kt index 84f72c697..6a0a33205 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageModuleManageSheet.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageModuleManageSheet.kt @@ -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 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, diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt index 2e7a41d1d..675673854 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageScreen.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt index f916f8b15..b916ef690 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/HomepageViewModel.kt @@ -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) diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetDetailPage.kt b/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetDetailPage.kt index 3e6f0f86f..b5fb74381 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetDetailPage.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetDetailPage.kt @@ -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 ) } ) diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetListPage.kt b/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetListPage.kt index ee703a5fb..6a9b73e1a 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetListPage.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/manage/SetListPage.kt @@ -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 ) } ) diff --git a/app/src/main/java/io/legado/app/ui/main/homepage/manage/SourceBrowseDetailPage.kt b/app/src/main/java/io/legado/app/ui/main/homepage/manage/SourceBrowseDetailPage.kt index 195c2fbe9..5139c24ac 100644 --- a/app/src/main/java/io/legado/app/ui/main/homepage/manage/SourceBrowseDetailPage.kt +++ b/app/src/main/java/io/legado/app/ui/main/homepage/manage/SourceBrowseDetailPage.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt b/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt index c57a0c336..31d7e7790 100644 --- a/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt @@ -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) ) } } diff --git a/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditScreen.kt b/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditScreen.kt index 840b4f124..9b5316134 100644 --- a/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditScreen.kt +++ b/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditScreen.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssSortScreen.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssSortScreen.kt index bce6868c3..c228f15b7 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssSortScreen.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssSortScreen.kt @@ -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) ) } diff --git a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt index a60b1ad29..c7640cc56 100644 --- a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt +++ b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt @@ -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" ) }, diff --git a/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt b/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt index bc922e627..3aca4ad5d 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt @@ -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) ) } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceScreen.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceScreen.kt index 993dc281b..c0c35b5ea 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceScreen.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceScreen.kt @@ -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 ) } ) diff --git a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt index 1167b7cfb..36f9a1b8c 100644 --- a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt +++ b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt @@ -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( diff --git a/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt b/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt index df59a4bdf..825783977 100644 --- a/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt +++ b/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt @@ -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, diff --git a/app/src/main/java/io/legado/app/ui/widget/components/AppSlider.kt b/app/src/main/java/io/legado/app/ui/widget/components/AppSlider.kt new file mode 100644 index 000000000..3b2acc148 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/AppSlider.kt @@ -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 = 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 + ) + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/EmptyMessage.kt b/app/src/main/java/io/legado/app/ui/widget/components/EmptyMessage.kt index c835f6747..8b81f5962 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/EmptyMessage.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/EmptyMessage.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/GroupManageBottomSheet.kt b/app/src/main/java/io/legado/app/ui/widget/components/GroupManageBottomSheet.kt index 5c1f1dfcf..3251bcbc9 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/GroupManageBottomSheet.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/GroupManageBottomSheet.kt @@ -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) ) } } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/JsonRawEditor.kt b/app/src/main/java/io/legado/app/ui/widget/components/JsonRawEditor.kt index 3f039e9ba..ce6da2a77 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/JsonRawEditor.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/JsonRawEditor.kt @@ -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 = "压缩" ) } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/ValueStepper.kt b/app/src/main/java/io/legado/app/ui/widget/components/ValueStepper.kt index c538322fa..ad02da8e8 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/ValueStepper.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/ValueStepper.kt @@ -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 ) } } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedTextButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedTextButton.kt deleted file mode 100644 index 973e5973a..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedTextButton.kt +++ /dev/null @@ -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) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/MediumIconButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/MediumIconButton.kt deleted file mode 100644 index b17a540bc..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/MediumIconButton.kt +++ /dev/null @@ -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 - ) - } - ) - } -} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/MediumTextButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/MediumTextButton.kt deleted file mode 100644 index 88bede26b..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/MediumTextButton.kt +++ /dev/null @@ -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) - ) - } - } -} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/SmallIconButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/SmallIconButton.kt deleted file mode 100644 index 4bb659a67..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/SmallIconButton.kt +++ /dev/null @@ -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 - ) - } - ) - } - } -} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/SmallTextButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/SmallTextButton.kt deleted file mode 100644 index 3fd6a42e3..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/SmallTextButton.kt +++ /dev/null @@ -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 - ) - } - } - } -} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedActionButtonCore.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/AnimatedActionButtonCore.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedActionButtonCore.kt rename to app/src/main/java/io/legado/app/ui/widget/components/button/series/AnimatedActionButtonCore.kt index 48b733ba5..dac4b704b 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedActionButtonCore.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/AnimatedActionButtonCore.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedIcon.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/AnimatedIcon.kt similarity index 94% rename from app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedIcon.kt rename to app/src/main/java/io/legado/app/ui/widget/components/button/series/AnimatedIcon.kt index ffbfaaeb7..4e0296ba4 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/button/AnimatedIcon.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/AnimatedIcon.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumAnimatedButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumAnimatedButton.kt new file mode 100644 index 000000000..4ba6e5f3f --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumAnimatedButton.kt @@ -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, + ) + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumOutlinedButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumOutlinedButton.kt new file mode 100644 index 000000000..827ba55e5 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumOutlinedButton.kt @@ -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) + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumPlainButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumPlainButton.kt new file mode 100644 index 000000000..946878215 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumPlainButton.kt @@ -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) + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumToggleButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumToggleButton.kt new file mode 100644 index 000000000..be39c468f --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumToggleButton.kt @@ -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, + ) + } + } + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumTonalButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumTonalButton.kt new file mode 100644 index 000000000..24d7bac4a --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/MediumTonalButton.kt @@ -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) + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallAnimatedButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallAnimatedButton.kt new file mode 100644 index 000000000..69172217c --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallAnimatedButton.kt @@ -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), + ) + } + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallOutlinedButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallOutlinedButton.kt new file mode 100644 index 000000000..24939120d --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallOutlinedButton.kt @@ -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) + } + } + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallPlainButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallPlainButton.kt new file mode 100644 index 000000000..839ff94a3 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallPlainButton.kt @@ -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) + } + } + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallToggleButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallToggleButton.kt new file mode 100644 index 000000000..77ce135fe --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallToggleButton.kt @@ -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), + ) + } + } + } + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallTonalButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallTonalButton.kt new file mode 100644 index 000000000..87709b123 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/components/button/series/SmallTonalButton.kt @@ -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) + } + } + } + } + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt b/app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt index 0560ef3c8..6d6de6098 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt @@ -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( diff --git a/app/src/main/java/io/legado/app/ui/widget/components/heatmap/HeatmapCalendarComponents.kt b/app/src/main/java/io/legado/app/ui/widget/components/heatmap/HeatmapCalendarComponents.kt index 0bef92c93..55d25ce8a 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/heatmap/HeatmapCalendarComponents.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/heatmap/HeatmapCalendarComponents.kt @@ -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, ) } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/importComponents/ImportComponents.kt b/app/src/main/java/io/legado/app/ui/widget/components/importComponents/ImportComponents.kt index e94779fe9..d0d9e8ba2 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/importComponents/ImportComponents.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/importComponents/ImportComponents.kt @@ -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 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 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 = "详情" ) } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/log/AppLogSheet.kt b/app/src/main/java/io/legado/app/ui/widget/components/log/AppLogSheet.kt index c32920750..4e48cb95c 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/log/AppLogSheet.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/log/AppLogSheet.kt @@ -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 ) } ) { diff --git a/app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/OptionSheet.kt b/app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/OptionSheet.kt index 8e5634d30..337bd2849 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/OptionSheet.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/OptionSheet.kt @@ -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( diff --git a/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleEditSheet.kt b/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleEditSheet.kt index 46f278a63..7ac8dd570 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleEditSheet.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/rules/RuleEditSheet.kt @@ -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 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 }) { diff --git a/app/src/main/java/io/legado/app/ui/widget/components/topbar/GlassMediumFlexibleTopAppBar.kt b/app/src/main/java/io/legado/app/ui/widget/components/topbar/GlassMediumFlexibleTopAppBar.kt index f063a8660..3610d664a 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/topbar/GlassMediumFlexibleTopAppBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/topbar/GlassMediumFlexibleTopAppBar.kt @@ -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 ) diff --git a/app/src/main/java/io/legado/app/ui/widget/components/topbar/TopBarButton.kt b/app/src/main/java/io/legado/app/ui/widget/components/topbar/TopBarButton.kt index e42304535..cc0abdae8 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/topbar/TopBarButton.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/topbar/TopBarButton.kt @@ -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, diff --git a/app/src/main/java/io/legado/app/web/socket/BookSearchWebSocket.kt b/app/src/main/java/io/legado/app/web/socket/BookSearchWebSocket.kt index c7228ec92..d70755ef0 100644 --- a/app/src/main/java/io/legado/app/web/socket/BookSearchWebSocket.kt +++ b/app/src/main/java/io/legado/app/web/socket/BookSearchWebSocket.kt @@ -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() 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 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c491743b6..36692b44c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -24,7 +24,7 @@ 去设置 无法跳转至设置界面 - 手动输入 + 输入URL 偏好对比度 来源:系统对比度 来源:手动输入 @@ -897,7 +897,7 @@ 正文标题 显示/隐藏 页眉与页脚 - 规则订阅 + 规则 添加大佬们提供的规则导入地址\n添加后点击可导入规则 拉取云端进度 覆盖云端进度 @@ -1643,6 +1643,8 @@ · 进度 %1$d/%2$d %1$s分组搜索结果为空,是否关闭精准搜索? %1$s分组搜索结果为空,是否切换到全部分组? + 列表布局 + 来源分组 下载与缓存 HTTP缓存 封面缓存 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 15842f874..c5bb91e01 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1679,6 +1679,8 @@ · Progress %1$d/%2$d %1$s group search returned no results. Disable precise search? %1$s group search returned no results. Switch to all groups? + List layout + Source grouped Download & Cache HTTP Cache Cover Cache