[优化] 界面优化
This commit is contained in:
@@ -274,16 +274,6 @@
|
|||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop" />
|
android:launchMode="singleTop" />
|
||||||
<!-- 导入书籍 -->
|
|
||||||
<activity
|
|
||||||
android:name=".ui.book.import.local.ImportBookActivity"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:launchMode="singleTop" />
|
|
||||||
<!-- 添加远程 -->
|
|
||||||
<activity
|
|
||||||
android:name=".ui.book.import.remote.RemoteBookActivity"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:launchMode="singleTop" />
|
|
||||||
<!-- 发现界面 -->
|
<!-- 发现界面 -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.book.explore.ExploreShowActivity"
|
android:name=".ui.book.explore.ExploreShowActivity"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import io.legado.app.help.http.okHttpClientManga
|
|||||||
import io.legado.app.ui.book.bookmark.AllBookmarkViewModel
|
import io.legado.app.ui.book.bookmark.AllBookmarkViewModel
|
||||||
import io.legado.app.ui.book.explore.ExploreShowViewModel
|
import io.legado.app.ui.book.explore.ExploreShowViewModel
|
||||||
import io.legado.app.ui.book.group.GroupViewModel
|
import io.legado.app.ui.book.group.GroupViewModel
|
||||||
|
import io.legado.app.ui.book.import.local.ImportBookViewModel
|
||||||
import io.legado.app.ui.book.import.remote.RemoteBookViewModel
|
import io.legado.app.ui.book.import.remote.RemoteBookViewModel
|
||||||
import io.legado.app.ui.book.info.BookInfoViewModel
|
import io.legado.app.ui.book.info.BookInfoViewModel
|
||||||
import io.legado.app.ui.book.readRecord.ReadRecordViewModel
|
import io.legado.app.ui.book.readRecord.ReadRecordViewModel
|
||||||
@@ -97,6 +98,7 @@ val appModule = module {
|
|||||||
viewModelOf(::ThemeConfigViewModel)
|
viewModelOf(::ThemeConfigViewModel)
|
||||||
viewModelOf(::BackupConfigViewModel)
|
viewModelOf(::BackupConfigViewModel)
|
||||||
viewModelOf(::TocViewModel)
|
viewModelOf(::TocViewModel)
|
||||||
|
viewModelOf(::ImportBookViewModel)
|
||||||
viewModelOf(::RemoteBookViewModel)
|
viewModelOf(::RemoteBookViewModel)
|
||||||
viewModelOf(::BookInfoViewModel)
|
viewModelOf(::BookInfoViewModel)
|
||||||
viewModelOf(::ExploreViewModel)
|
viewModelOf(::ExploreViewModel)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import io.legado.app.data.entities.Bookmark
|
import io.legado.app.data.entities.Bookmark
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.theme.ThemeResolver
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||||
import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
|
import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
|
||||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||||
import io.legado.app.ui.widget.components.AppScaffold
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
@@ -242,7 +243,7 @@ fun AllBookmarkScreen(
|
|||||||
FastScrollLazyColumn(
|
FastScrollLazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentPadding = adaptiveContentPaddingOnlyVertical(
|
contentPadding = adaptiveContentPadding(
|
||||||
top = paddingValues.calculateTopPadding(),
|
top = paddingValues.calculateTopPadding(),
|
||||||
bottom = 120.dp
|
bottom = 120.dp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -235,7 +235,6 @@ fun ExploreShowScreen(
|
|||||||
|
|
||||||
AppModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
show = showKindSheet,
|
show = showKindSheet,
|
||||||
containerColor = LegadoTheme.colorScheme.surface,
|
|
||||||
onDismissRequest = { showKindSheet = false }
|
onDismissRequest = { showKindSheet = false }
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
package io.legado.app.ui.book.import
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import androidx.appcompat.widget.SearchView
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import io.legado.app.R
|
|
||||||
import io.legado.app.base.VMBaseActivity
|
|
||||||
import io.legado.app.constant.AppPattern
|
|
||||||
import io.legado.app.data.appDb
|
|
||||||
import io.legado.app.data.entities.Book
|
|
||||||
import io.legado.app.databinding.ActivityImportBookBinding
|
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.lib.dialogs.alert
|
|
||||||
import io.legado.app.lib.dialogs.selector
|
|
||||||
//import io.legado.app.lib.theme.primaryTextColor
|
|
||||||
import io.legado.app.model.localBook.LocalBook
|
|
||||||
import io.legado.app.ui.file.HandleFileContract
|
|
||||||
import io.legado.app.utils.ArchiveUtils
|
|
||||||
import io.legado.app.utils.FileDoc
|
|
||||||
import io.legado.app.utils.hideSoftInput
|
|
||||||
import io.legado.app.utils.shouldHideSoftInput
|
|
||||||
import io.legado.app.utils.startActivityForBook
|
|
||||||
import io.legado.app.utils.toastOnUi
|
|
||||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
abstract class BaseImportBookActivity<VM : ViewModel> :
|
|
||||||
VMBaseActivity<ActivityImportBookBinding, VM>() {
|
|
||||||
|
|
||||||
final override val binding by viewBinding(ActivityImportBookBinding::inflate)
|
|
||||||
|
|
||||||
private var localBookTreeSelectListener: ((Boolean) -> Unit)? = null
|
|
||||||
protected val searchView: SearchView by lazy {
|
|
||||||
binding.titleBar.findViewById(R.id.search_view)
|
|
||||||
}
|
|
||||||
|
|
||||||
val localBookTreeSelect = registerForActivityResult(HandleFileContract()) {
|
|
||||||
it.uri?.let { treeUri ->
|
|
||||||
AppConfig.defaultBookTreeUri = treeUri.toString()
|
|
||||||
localBookTreeSelectListener?.invoke(true)
|
|
||||||
} ?: localBookTreeSelectListener?.invoke(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
initSearchView()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置书籍保存位置
|
|
||||||
*/
|
|
||||||
protected suspend fun setBookStorage() = suspendCoroutine { block ->
|
|
||||||
localBookTreeSelectListener = {
|
|
||||||
localBookTreeSelectListener = null
|
|
||||||
block.resume(it)
|
|
||||||
}
|
|
||||||
//测试书籍保存位置是否设置
|
|
||||||
if (!AppConfig.defaultBookTreeUri.isNullOrBlank()) {
|
|
||||||
localBookTreeSelectListener = null
|
|
||||||
block.resume(true)
|
|
||||||
return@suspendCoroutine
|
|
||||||
}
|
|
||||||
//测试读写??
|
|
||||||
val storageHelp = String(assets.open("storageHelp.md").readBytes())
|
|
||||||
val hint = getString(R.string.select_book_folder)
|
|
||||||
alert(hint, storageHelp) {
|
|
||||||
okButton {
|
|
||||||
localBookTreeSelect.launch {
|
|
||||||
title = hint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cancelButton {
|
|
||||||
localBookTreeSelectListener = null
|
|
||||||
block.resume(false)
|
|
||||||
}
|
|
||||||
onCancelled {
|
|
||||||
localBookTreeSelectListener = null
|
|
||||||
block.resume(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun onSearchTextChange(newText: String?)
|
|
||||||
|
|
||||||
protected fun startReadBook(book: Book) {
|
|
||||||
startActivityForBook(book)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun onArchiveFileClick(fileDoc: FileDoc) {
|
|
||||||
val fileNames = ArchiveUtils.getArchiveFilesName(fileDoc) {
|
|
||||||
it.matches(AppPattern.bookFileRegex)
|
|
||||||
}
|
|
||||||
if (fileNames.size == 1) {
|
|
||||||
val name = fileNames[0]
|
|
||||||
appDb.bookDao.getBookByFileName(name)?.let {
|
|
||||||
startReadBook(it)
|
|
||||||
} ?: showImportAlert(fileDoc, name)
|
|
||||||
} else {
|
|
||||||
showSelectBookReadAlert(fileDoc, fileNames)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showSelectBookReadAlert(fileDoc: FileDoc, fileNames: List<String>) {
|
|
||||||
if (fileNames.isEmpty()) {
|
|
||||||
toastOnUi(R.string.unsupport_archivefile_entry)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
selector(
|
|
||||||
R.string.start_read,
|
|
||||||
fileNames
|
|
||||||
) { _, name, _ ->
|
|
||||||
appDb.bookDao.getBookByFileName(name)?.let {
|
|
||||||
startReadBook(it)
|
|
||||||
} ?: showImportAlert(fileDoc, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 添加压缩包内指定文件到书架 */
|
|
||||||
private inline fun addArchiveToBookShelf(
|
|
||||||
fileDoc: FileDoc,
|
|
||||||
fileName: String,
|
|
||||||
onSuccess: (Book) -> Unit
|
|
||||||
) {
|
|
||||||
LocalBook.importArchiveFile(fileDoc.uri, fileName) {
|
|
||||||
it.contains(fileName)
|
|
||||||
}.firstOrNull()?.run {
|
|
||||||
onSuccess.invoke(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 提示是否重新导入所点击的压缩文件 */
|
|
||||||
private fun showImportAlert(fileDoc: FileDoc, fileName: String) {
|
|
||||||
alert(
|
|
||||||
R.string.draw,
|
|
||||||
R.string.no_book_found_bookshelf
|
|
||||||
) {
|
|
||||||
okButton {
|
|
||||||
addArchiveToBookShelf(fileDoc, fileName) {
|
|
||||||
startReadBook(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
noButton()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initSearchView() {
|
|
||||||
//searchView.applyTint(primaryTextColor)
|
|
||||||
searchView.isSubmitButtonEnabled = true
|
|
||||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
|
||||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String?): Boolean {
|
|
||||||
onSearchTextChange(newText)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,497 @@
|
|||||||
|
package io.legado.app.ui.book.import.local
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
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.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material.icons.filled.ChevronRight
|
||||||
|
import androidx.compose.material.icons.filled.CloudDownload
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Folder
|
||||||
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
|
import androidx.compose.material.icons.outlined.Book
|
||||||
|
import androidx.compose.material.icons.outlined.Description
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import io.legado.app.R
|
||||||
|
import io.legado.app.constant.AppConst
|
||||||
|
import io.legado.app.constant.AppPattern
|
||||||
|
import io.legado.app.data.appDb
|
||||||
|
import io.legado.app.data.entities.Book
|
||||||
|
import io.legado.app.help.config.AppConfig
|
||||||
|
import io.legado.app.lib.dialogs.alert
|
||||||
|
import io.legado.app.lib.dialogs.selector
|
||||||
|
import io.legado.app.lib.permission.Permissions
|
||||||
|
import io.legado.app.lib.permission.PermissionsCompat
|
||||||
|
import io.legado.app.model.localBook.LocalBook
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
|
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||||
|
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.SmallTonalIconButton
|
||||||
|
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||||
|
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
|
||||||
|
import io.legado.app.ui.widget.components.list.ListScaffold
|
||||||
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
import io.legado.app.utils.ConvertUtils
|
||||||
|
import io.legado.app.utils.ArchiveUtils
|
||||||
|
import io.legado.app.utils.FileDoc
|
||||||
|
import io.legado.app.utils.isContentScheme
|
||||||
|
import io.legado.app.utils.isUri
|
||||||
|
import io.legado.app.utils.startActivityForBook
|
||||||
|
import io.legado.app.utils.takePersistablePermissionSafely
|
||||||
|
import io.legado.app.utils.toastOnUi
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun ImportBookContent(
|
||||||
|
state: ImportBookUiState,
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
onSearchToggle: (Boolean) -> Unit,
|
||||||
|
onSearchQueryChange: (String) -> Unit,
|
||||||
|
onSelectFolder: () -> Unit,
|
||||||
|
onScanFolder: () -> Unit,
|
||||||
|
onImportFileName: () -> Unit,
|
||||||
|
onSortChange: (Int) -> Unit,
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateToLevel: (Int) -> Unit,
|
||||||
|
onSelectAll: () -> Unit,
|
||||||
|
onSelectInvert: () -> Unit,
|
||||||
|
onAddToBookshelf: () -> Unit,
|
||||||
|
onDeleteSelection: () -> Unit,
|
||||||
|
onItemClick: (ImportBook) -> Unit
|
||||||
|
) {
|
||||||
|
ListScaffold(
|
||||||
|
title = stringResource(R.string.local_book),
|
||||||
|
state = state,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
onSearchToggle = onSearchToggle,
|
||||||
|
onSearchQueryChange = onSearchQueryChange,
|
||||||
|
searchPlaceholder = stringResource(R.string.screen),
|
||||||
|
topBarActions = {
|
||||||
|
TopBarActionButton(
|
||||||
|
onClick = onSelectFolder,
|
||||||
|
imageVector = Icons.Default.FolderOpen,
|
||||||
|
contentDescription = stringResource(R.string.select_folder)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
dropDownMenuContent = { dismiss ->
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.sort_by_name),
|
||||||
|
onClick = {
|
||||||
|
onSortChange(0)
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
if (state.sort == 0) {
|
||||||
|
Icon(Icons.Default.Check, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.sort_by_size),
|
||||||
|
onClick = {
|
||||||
|
onSortChange(1)
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
if (state.sort == 1) {
|
||||||
|
Icon(Icons.Default.Check, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.sort_by_time),
|
||||||
|
onClick = {
|
||||||
|
onSortChange(2)
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
if (state.sort == 2) {
|
||||||
|
Icon(Icons.Default.Check, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.scan_folder),
|
||||||
|
onClick = {
|
||||||
|
onScanFolder()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.import_file_name),
|
||||||
|
onClick = {
|
||||||
|
onImportFileName()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
bottomContent = {
|
||||||
|
ImportPathNavigationBar(
|
||||||
|
pathNames = state.pathNames,
|
||||||
|
canGoBack = state.canGoBack,
|
||||||
|
onNavigateBack = onNavigateBack,
|
||||||
|
onNavigateToLevel = onNavigateToLevel
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectionActions = SelectionActions(
|
||||||
|
onSelectAll = onSelectAll,
|
||||||
|
onSelectInvert = onSelectInvert,
|
||||||
|
primaryAction = ActionItem(
|
||||||
|
text = stringResource(R.string.add_to_bookshelf),
|
||||||
|
icon = { Icon(Icons.Default.CloudDownload, null) },
|
||||||
|
onClick = onAddToBookshelf
|
||||||
|
),
|
||||||
|
secondaryActions = listOf(
|
||||||
|
ActionItem(
|
||||||
|
text = stringResource(R.string.delete),
|
||||||
|
icon = { Icon(Icons.Default.Delete, null) },
|
||||||
|
onClick = onDeleteSelection
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
onAddClick = null
|
||||||
|
) { paddingValues ->
|
||||||
|
val refreshState = rememberPullToRefreshState()
|
||||||
|
PullToRefreshBox(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues),
|
||||||
|
isRefreshing = state.isLoading,
|
||||||
|
state = refreshState,
|
||||||
|
onRefresh = onScanFolder
|
||||||
|
) {
|
||||||
|
when {
|
||||||
|
state.items.isEmpty() && state.isLoading -> {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||||
|
}
|
||||||
|
|
||||||
|
state.items.isEmpty() -> {
|
||||||
|
EmptyMessageView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
message = stringResource(R.string.empty_msg_import_book)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||||
|
items(state.items, key = { it.selectionId }) { item ->
|
||||||
|
ImportBookItem(
|
||||||
|
modifier = Modifier.animateItem(),
|
||||||
|
item = item,
|
||||||
|
isSelected = item.selectionId in state.selectedIds,
|
||||||
|
onClick = { onItemClick(item) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ImportBookScreen(
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
viewModel: ImportBookViewModel = koinViewModel()
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
var showFolderPicker by remember { mutableStateOf(false) }
|
||||||
|
var showImportFileNameDialog by remember { mutableStateOf(false) }
|
||||||
|
var fileNameJs by remember { mutableStateOf(AppConfig.bookImportFileName.orEmpty()) }
|
||||||
|
var pickerTarget by remember { mutableStateOf(ImportFolderPickTarget.IMPORT_FOLDER) }
|
||||||
|
val selectDocTree = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.OpenDocumentTree()
|
||||||
|
) { uri ->
|
||||||
|
viewModel.dispatch(ImportBookIntent.FolderPicked(uri, pickerTarget))
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(viewModel) {
|
||||||
|
viewModel.dispatch(ImportBookIntent.Initialize)
|
||||||
|
viewModel.effects.collect { effect ->
|
||||||
|
when (effect) {
|
||||||
|
is ImportBookEffect.RequestFolderPicker -> {
|
||||||
|
pickerTarget = effect.target
|
||||||
|
selectDocTree.launch(effect.initialUri)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ImportBookEffect.OpenBook -> {
|
||||||
|
context.startActivityForBook(effect.book)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ImportBookEffect.ShowArchiveEntries -> {
|
||||||
|
context.selector(R.string.start_read, effect.fileNames) { _, name, _ ->
|
||||||
|
viewModel.dispatch(
|
||||||
|
ImportBookIntent.ArchiveEntrySelected(effect.fileDoc, name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ImportBookEffect.ShowImportArchiveDialog -> {
|
||||||
|
context.alert(R.string.draw, R.string.no_book_found_bookshelf) {
|
||||||
|
okButton {
|
||||||
|
viewModel.dispatch(
|
||||||
|
ImportBookIntent.ImportArchiveConfirmed(
|
||||||
|
effect.fileDoc,
|
||||||
|
effect.fileName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
noButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ImportBookEffect.ShowToastRes -> {
|
||||||
|
context.toastOnUi(effect.resId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePickerSheet(
|
||||||
|
show = showFolderPicker,
|
||||||
|
onDismissRequest = { showFolderPicker = false },
|
||||||
|
title = stringResource(R.string.select_folder),
|
||||||
|
onSelectSysDir = {
|
||||||
|
showFolderPicker = false
|
||||||
|
viewModel.dispatch(ImportBookIntent.SelectFolderClick)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
AppAlertDialog(
|
||||||
|
show = showImportFileNameDialog,
|
||||||
|
onDismissRequest = { showImportFileNameDialog = false },
|
||||||
|
title = stringResource(R.string.import_file_name),
|
||||||
|
content = {
|
||||||
|
AppText("Use js to parse file name from src, then assign name/author.")
|
||||||
|
OutlinedTextField(
|
||||||
|
value = fileNameJs,
|
||||||
|
onValueChange = { fileNameJs = it },
|
||||||
|
label = { AppText("js") }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmText = stringResource(android.R.string.ok),
|
||||||
|
onConfirm = {
|
||||||
|
AppConfig.bookImportFileName = fileNameJs
|
||||||
|
showImportFileNameDialog = false
|
||||||
|
},
|
||||||
|
dismissText = stringResource(android.R.string.cancel),
|
||||||
|
onDismiss = { showImportFileNameDialog = false }
|
||||||
|
)
|
||||||
|
|
||||||
|
ImportBookContent(
|
||||||
|
state = uiState,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
onSearchToggle = { viewModel.dispatch(ImportBookIntent.SearchToggle(it)) },
|
||||||
|
onSearchQueryChange = { viewModel.dispatch(ImportBookIntent.SearchQueryChange(it)) },
|
||||||
|
onSelectFolder = { showFolderPicker = true },
|
||||||
|
onScanFolder = { viewModel.dispatch(ImportBookIntent.ScanFolder) },
|
||||||
|
onImportFileName = {
|
||||||
|
fileNameJs = AppConfig.bookImportFileName.orEmpty()
|
||||||
|
showImportFileNameDialog = true
|
||||||
|
},
|
||||||
|
onSortChange = { viewModel.dispatch(ImportBookIntent.SortChange(it)) },
|
||||||
|
onNavigateBack = { viewModel.dispatch(ImportBookIntent.NavigateBack) },
|
||||||
|
onNavigateToLevel = { viewModel.dispatch(ImportBookIntent.NavigateToLevel(it)) },
|
||||||
|
onSelectAll = { viewModel.dispatch(ImportBookIntent.SelectAll) },
|
||||||
|
onSelectInvert = { viewModel.dispatch(ImportBookIntent.SelectInvert) },
|
||||||
|
onAddToBookshelf = { viewModel.dispatch(ImportBookIntent.AddToBookshelf) },
|
||||||
|
onDeleteSelection = { viewModel.dispatch(ImportBookIntent.DeleteSelection) },
|
||||||
|
onItemClick = { viewModel.dispatch(ImportBookIntent.ItemClick(it)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ImportPathNavigationBar(
|
||||||
|
pathNames: List<String>,
|
||||||
|
canGoBack: Boolean,
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateToLevel: (Int) -> Unit
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 16.dp, end = 16.dp, bottom = 4.dp)
|
||||||
|
.animateContentSize(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
GlassCard(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
containerColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
|
) {
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
itemsIndexed(pathNames) { index, name ->
|
||||||
|
val isLast = index == pathNames.lastIndex
|
||||||
|
AppText(
|
||||||
|
text = name,
|
||||||
|
style = LegadoTheme.typography.labelSmall,
|
||||||
|
fontWeight = if (isLast) FontWeight.SemiBold else FontWeight.Medium,
|
||||||
|
color = if (isLast) {
|
||||||
|
LegadoTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(MaterialTheme.shapes.small)
|
||||||
|
.then(if (!isLast) Modifier.clickable { onNavigateToLevel(index) } else Modifier)
|
||||||
|
.padding(horizontal = 4.dp, vertical = 4.dp)
|
||||||
|
)
|
||||||
|
if (!isLast) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.ChevronRight,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(12.dp),
|
||||||
|
tint = LegadoTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canGoBack) {
|
||||||
|
SmallTonalIconButton(
|
||||||
|
onClick = onNavigateBack,
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
contentDescription = "back"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ImportBookItem(
|
||||||
|
modifier: Modifier,
|
||||||
|
item: ImportBook,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val containerColor = if (isSelected) {
|
||||||
|
LegadoTheme.colorScheme.secondaryContainer
|
||||||
|
} else {
|
||||||
|
LegadoTheme.colorScheme.surfaceContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
GlassCard(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||||
|
onClick = onClick,
|
||||||
|
containerColor = containerColor
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = when {
|
||||||
|
item.isDir -> Icons.Default.Folder
|
||||||
|
item.isOnBookShelf -> Icons.Outlined.Book
|
||||||
|
else -> Icons.Outlined.Description
|
||||||
|
},
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
tint = if (item.isDir) {
|
||||||
|
LegadoTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
AppText(
|
||||||
|
text = item.name,
|
||||||
|
style = LegadoTheme.typography.bodyMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!item.isDir) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
TextCard(
|
||||||
|
text = item.name.substringAfterLast('.', "").uppercase(),
|
||||||
|
textStyle = LegadoTheme.typography.labelSmall,
|
||||||
|
horizontalPadding = 4.dp,
|
||||||
|
verticalPadding = 2.dp,
|
||||||
|
cornerRadius = 4.dp,
|
||||||
|
icon = null,
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainerHighest
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
|
AppText(
|
||||||
|
text = "${ConvertUtils.formatFileSize(item.size)} - ${AppConst.dateFormat.format(item.lastModified)}",
|
||||||
|
style = LegadoTheme.typography.labelMedium,
|
||||||
|
color = LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.isDir && !item.isOnBookShelf) {
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Icon(
|
||||||
|
imageVector = if (isSelected) Icons.Default.Check else Icons.Default.ChevronRight,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = LegadoTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,15 +145,15 @@ fun BookInfoEditContent(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
icon = Icons.Default.ImageSearch
|
imageVector = Icons.Default.ImageSearch
|
||||||
)
|
)
|
||||||
MediumOutlinedIconButton(
|
MediumOutlinedIconButton(
|
||||||
onClick = { selectCover.launch() },
|
onClick = { selectCover.launch() },
|
||||||
icon = Icons.Default.FolderOpen
|
imageVector = Icons.Default.FolderOpen
|
||||||
)
|
)
|
||||||
MediumOutlinedIconButton(
|
MediumOutlinedIconButton(
|
||||||
onClick = { viewModel.resetCover() },
|
onClick = { viewModel.resetCover() },
|
||||||
icon = Icons.Default.Replay
|
imageVector = Icons.Default.Replay
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ import cn.hutool.core.date.DateUtil
|
|||||||
import io.legado.app.data.entities.readRecord.ReadRecord
|
import io.legado.app.data.entities.readRecord.ReadRecord
|
||||||
import io.legado.app.data.entities.readRecord.ReadRecordDetail
|
import io.legado.app.data.entities.readRecord.ReadRecordDetail
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||||
|
import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
|
||||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||||
import io.legado.app.ui.widget.CollapsibleHeader
|
import io.legado.app.ui.widget.CollapsibleHeader
|
||||||
import io.legado.app.ui.widget.components.AppScaffold
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
@@ -78,9 +80,12 @@ import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
|||||||
import io.legado.app.ui.widget.components.button.AppIconButton
|
import io.legado.app.ui.widget.components.button.AppIconButton
|
||||||
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||||
import io.legado.app.ui.widget.components.card.GlassCard
|
import io.legado.app.ui.widget.components.card.GlassCard
|
||||||
|
import io.legado.app.ui.widget.components.card.TextCard
|
||||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
||||||
import io.legado.app.ui.widget.components.cover.Cover
|
import io.legado.app.ui.widget.components.cover.Cover
|
||||||
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar
|
import io.legado.app.ui.widget.components.heatmap.HEATMAP_CALENDAR_TITLE
|
||||||
|
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarEndAction
|
||||||
|
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarStartAction
|
||||||
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
|
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
|
||||||
import io.legado.app.ui.widget.components.heatmap.HeatmapLegend
|
import io.legado.app.ui.widget.components.heatmap.HeatmapLegend
|
||||||
import io.legado.app.ui.widget.components.heatmap.HeatmapMode
|
import io.legado.app.ui.widget.components.heatmap.HeatmapMode
|
||||||
@@ -91,6 +96,7 @@ import io.legado.app.ui.widget.components.heatmap.rememberDateRange
|
|||||||
import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange
|
import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange
|
||||||
import io.legado.app.ui.widget.components.heatmap.rememberWeeks
|
import io.legado.app.ui.widget.components.heatmap.rememberWeeks
|
||||||
import io.legado.app.ui.widget.components.icon.AppIcon
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
|
import io.legado.app.ui.widget.components.list.TopFloatingStickyItem
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.swipe.SwipeAction
|
import io.legado.app.ui.widget.components.swipe.SwipeAction
|
||||||
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
|
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
|
||||||
@@ -118,6 +124,7 @@ fun ReadRecordScreen(
|
|||||||
val displayMode by viewModel.displayMode.collectAsState()
|
val displayMode by viewModel.displayMode.collectAsState()
|
||||||
var showSearch by remember { mutableStateOf(false) }
|
var showSearch by remember { mutableStateOf(false) }
|
||||||
var showCalendar by remember { mutableStateOf(false) }
|
var showCalendar by remember { mutableStateOf(false) }
|
||||||
|
var heatmapMode by remember { mutableStateOf(HeatmapMode.COUNT) }
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
|
|
||||||
@@ -131,6 +138,36 @@ fun ReadRecordScreen(
|
|||||||
pendingDeleteAction = action
|
pendingDeleteAction = action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val stickyDate by remember(displayMode, listState) {
|
||||||
|
derivedStateOf {
|
||||||
|
if (displayMode == DisplayMode.LATEST) return@derivedStateOf null
|
||||||
|
val stickyKey = listState.layoutInfo.visibleItemsInfo
|
||||||
|
.firstOrNull { info ->
|
||||||
|
val key = info.key.toString()
|
||||||
|
key.startsWith("header_") ||
|
||||||
|
key.startsWith("timeline_header_") ||
|
||||||
|
key.startsWith("agg_item_") ||
|
||||||
|
key.startsWith("timeline_item_")
|
||||||
|
}?.key?.toString() ?: return@derivedStateOf null
|
||||||
|
|
||||||
|
when {
|
||||||
|
stickyKey.startsWith("header_") -> stickyKey.removePrefix("header_")
|
||||||
|
stickyKey.startsWith("timeline_header_") -> stickyKey.removePrefix("timeline_header_")
|
||||||
|
stickyKey.startsWith("agg_item_") -> stickyKey.removePrefix("agg_item_").substringBefore("|")
|
||||||
|
stickyKey.startsWith("timeline_item_") -> stickyKey.removePrefix("timeline_item_").substringBefore("|")
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val floatingDate by remember(stickyDate, listState, displayMode) {
|
||||||
|
derivedStateOf {
|
||||||
|
if (displayMode == DisplayMode.LATEST) return@derivedStateOf null
|
||||||
|
if (stickyDate == null) return@derivedStateOf null
|
||||||
|
val shouldStick = listState.firstVisibleItemIndex > 1 ||
|
||||||
|
listState.firstVisibleItemScrollOffset > 24
|
||||||
|
if (shouldStick) stickyDate else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(state.searchKey) {
|
LaunchedEffect(state.searchKey) {
|
||||||
if (state.searchKey.isNullOrBlank()) {
|
if (state.searchKey.isNullOrBlank()) {
|
||||||
@@ -198,9 +235,6 @@ fun ReadRecordScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
Column(modifier = Modifier
|
|
||||||
.padding(padding)
|
|
||||||
.fillMaxSize()) {
|
|
||||||
val contentState = when {
|
val contentState = when {
|
||||||
state.isLoading -> "LOADING"
|
state.isLoading -> "LOADING"
|
||||||
(displayMode == DisplayMode.AGGREGATE && state.groupedRecords.isEmpty()) ||
|
(displayMode == DisplayMode.AGGREGATE && state.groupedRecords.isEmpty()) ||
|
||||||
@@ -215,7 +249,12 @@ fun ReadRecordScreen(
|
|||||||
when (targetState) {
|
when (targetState) {
|
||||||
"LOADING" -> {
|
"LOADING" -> {
|
||||||
EmptyMessageView(
|
EmptyMessageView(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(
|
||||||
|
top = padding.calculateTopPadding(),
|
||||||
|
bottom = padding.calculateBottomPadding()
|
||||||
|
),
|
||||||
message = "加载中",
|
message = "加载中",
|
||||||
isLoading = true
|
isLoading = true
|
||||||
)
|
)
|
||||||
@@ -223,16 +262,25 @@ fun ReadRecordScreen(
|
|||||||
|
|
||||||
"EMPTY" -> {
|
"EMPTY" -> {
|
||||||
EmptyMessageView(
|
EmptyMessageView(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(
|
||||||
|
top = padding.calculateTopPadding(),
|
||||||
|
bottom = padding.calculateBottomPadding()
|
||||||
|
),
|
||||||
message = "没有记录"
|
message = "没有记录"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
"CONTENT" -> {
|
"CONTENT" -> {
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
contentPadding = adaptiveContentPaddingOnlyVertical(
|
||||||
|
top = padding.calculateTopPadding(),
|
||||||
|
bottom = padding.calculateBottomPadding() + 16.dp
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
item(key = "summary_card") {
|
item(key = "summary_card") {
|
||||||
SummarySection(state, viewModel)
|
SummarySection(state, viewModel)
|
||||||
@@ -255,6 +303,32 @@ fun ReadRecordScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TopFloatingStickyItem(
|
||||||
|
item = floatingDate,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
top = padding.calculateTopPadding() + 4.dp,
|
||||||
|
start = 8.dp
|
||||||
|
)
|
||||||
|
) { date ->
|
||||||
|
val text = buildString {
|
||||||
|
append(formatFriendlyDate(date))
|
||||||
|
if (displayMode == DisplayMode.AGGREGATE) {
|
||||||
|
val dailyTotal = state.groupedRecords[date]?.sumOf { it.readTime } ?: 0L
|
||||||
|
append(" · ")
|
||||||
|
append(formatDuring(dailyTotal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextCard(
|
||||||
|
text = text,
|
||||||
|
textStyle = LegadoTheme.typography.labelLarge,
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.cardContainer,
|
||||||
|
contentColor = LegadoTheme.colorScheme.onCardContainer,
|
||||||
|
cornerRadius = 8.dp,
|
||||||
|
horizontalPadding = 8.dp,
|
||||||
|
verticalPadding = 8.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,19 +366,31 @@ fun ReadRecordScreen(
|
|||||||
|
|
||||||
AppModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
show = showCalendar,
|
show = showCalendar,
|
||||||
onDismissRequest = { showCalendar = false }
|
onDismissRequest = { showCalendar = false },
|
||||||
|
title = HEATMAP_CALENDAR_TITLE,
|
||||||
|
startAction = {
|
||||||
|
HeatmapCalendarStartAction(
|
||||||
|
currentMode = heatmapMode,
|
||||||
|
onModeChanged = { heatmapMode = it }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
endAction = {
|
||||||
|
HeatmapCalendarEndAction(
|
||||||
|
onClearDate = {
|
||||||
|
viewModel.setSelectedDate(null)
|
||||||
|
showCalendar = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
HeatmapCalendarSection(
|
HeatmapCalendarSection(
|
||||||
dailyReadCounts = state.dailyReadCounts,
|
dailyReadCounts = state.dailyReadCounts,
|
||||||
dailyReadTimes = state.dailyReadTimes,
|
dailyReadTimes = state.dailyReadTimes,
|
||||||
|
currentMode = heatmapMode,
|
||||||
selectedDate = state.selectedDate,
|
selectedDate = state.selectedDate,
|
||||||
onDateSelected = { date ->
|
onDateSelected = { date ->
|
||||||
viewModel.setSelectedDate(date)
|
viewModel.setSelectedDate(date)
|
||||||
showCalendar = false
|
showCalendar = false
|
||||||
},
|
|
||||||
onClearDate = {
|
|
||||||
viewModel.setSelectedDate(null)
|
|
||||||
showCalendar = false
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -404,13 +490,11 @@ fun HeatmapCalendarSection(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
dailyReadCounts: Map<LocalDate, Int>,
|
dailyReadCounts: Map<LocalDate, Int>,
|
||||||
dailyReadTimes: Map<LocalDate, Long>,
|
dailyReadTimes: Map<LocalDate, Long>,
|
||||||
|
currentMode: HeatmapMode,
|
||||||
selectedDate: LocalDate?,
|
selectedDate: LocalDate?,
|
||||||
onDateSelected: (LocalDate) -> Unit,
|
onDateSelected: (LocalDate) -> Unit,
|
||||||
onClearDate: () -> Unit,
|
|
||||||
config: HeatmapConfig = HeatmapConfig()
|
config: HeatmapConfig = HeatmapConfig()
|
||||||
) {
|
) {
|
||||||
var currentMode by remember { mutableStateOf(HeatmapMode.COUNT) }
|
|
||||||
|
|
||||||
val (startDate, endDate) = rememberDateRange(dailyReadCounts, dailyReadTimes)
|
val (startDate, endDate) = rememberDateRange(dailyReadCounts, dailyReadTimes)
|
||||||
val days = rememberDaysInRange(startDate, endDate)
|
val days = rememberDaysInRange(startDate, endDate)
|
||||||
val weeks = rememberWeeks(days, startDate)
|
val weeks = rememberWeeks(days, startDate)
|
||||||
@@ -452,14 +536,6 @@ fun HeatmapCalendarSection(
|
|||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
.padding(bottom = 32.dp)
|
.padding(bottom = 32.dp)
|
||||||
) {
|
) {
|
||||||
HeatmapCalendarTopBar(
|
|
||||||
currentMode = currentMode,
|
|
||||||
onModeChanged = { currentMode = it },
|
|
||||||
onClearDate = onClearDate
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
WeekdayLabelsColumn(
|
WeekdayLabelsColumn(
|
||||||
cellSize = config.cellSize,
|
cellSize = config.cellSize,
|
||||||
@@ -543,10 +619,13 @@ fun LazyListScope.renderListByMode(
|
|||||||
when (displayMode) {
|
when (displayMode) {
|
||||||
DisplayMode.AGGREGATE -> {
|
DisplayMode.AGGREGATE -> {
|
||||||
state.groupedRecords.forEach { (date, details) ->
|
state.groupedRecords.forEach { (date, details) ->
|
||||||
stickyHeader(key = "header_$date") {
|
item(key = "header_$date") {
|
||||||
DateHeader(date, details.sumOf { it.readTime })
|
DateHeader(date, details.sumOf { it.readTime })
|
||||||
}
|
}
|
||||||
items(items = details, key = { "${it.bookName}_${it.bookAuthor}_${it.date}" }) { detail ->
|
items(
|
||||||
|
items = details,
|
||||||
|
key = { "agg_item_${date}|${it.bookName}_${it.bookAuthor}_${it.date}" }
|
||||||
|
) { detail ->
|
||||||
SwipeActionContainer(
|
SwipeActionContainer(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
startAction = SwipeAction(
|
startAction = SwipeAction(
|
||||||
@@ -568,8 +647,8 @@ fun LazyListScope.renderListByMode(
|
|||||||
|
|
||||||
DisplayMode.TIMELINE -> {
|
DisplayMode.TIMELINE -> {
|
||||||
state.timelineRecords.forEach { (date, sessions) ->
|
state.timelineRecords.forEach { (date, sessions) ->
|
||||||
stickyHeader(key = "timeline_header_$date") { DateHeader(date) }
|
item(key = "timeline_header_$date") { DateHeader(date) }
|
||||||
items(items = sessions, key = { it.id }) { session ->
|
items(items = sessions, key = { "timeline_item_${date}|${it.id}" }) { session ->
|
||||||
SwipeActionContainer(
|
SwipeActionContainer(
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
startAction = SwipeAction(
|
startAction = SwipeAction(
|
||||||
@@ -637,7 +716,7 @@ fun LatestReadItem(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.adaptiveHorizontalPadding(vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Cover(coverPath)
|
Cover(coverPath)
|
||||||
@@ -798,7 +877,7 @@ fun ReadRecordItem(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.adaptiveHorizontalPadding(vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Cover(coverPath)
|
Cover(coverPath)
|
||||||
@@ -877,7 +956,7 @@ fun ReadingSummaryCard(
|
|||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.adaptiveHorizontalPadding(vertical = 8.dp),
|
||||||
containerColor = LegadoTheme.colorScheme.surfaceContainer
|
containerColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ fun SearchHistoryList(
|
|||||||
trailingContent = {
|
trailingContent = {
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = { onDeleteHistory(item) },
|
onClick = { onDeleteHistory(item) },
|
||||||
icon = Icons.Default.Close,
|
imageVector = Icons.Default.Close,
|
||||||
contentDescription = "删除"
|
contentDescription = "删除"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -366,7 +366,7 @@ fun SearchHistoryList(
|
|||||||
MediumOutlinedButton(
|
MediumOutlinedButton(
|
||||||
onClick = onClearHistory,
|
onClick = onClearHistory,
|
||||||
modifier = Modifier.fillMaxWidth(0.6f),
|
modifier = Modifier.fillMaxWidth(0.6f),
|
||||||
icon = Icons.Outlined.DeleteSweep,
|
imageVector = Icons.Outlined.DeleteSweep,
|
||||||
text = "清除搜索历史"
|
text = "清除搜索历史"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ fun TocScreen(
|
|||||||
SmallOutlinedIconToggleButton(
|
SmallOutlinedIconToggleButton(
|
||||||
checked = showVolumeMenu,
|
checked = showVolumeMenu,
|
||||||
onCheckedChange = { showVolumeMenu = it },
|
onCheckedChange = { showVolumeMenu = it },
|
||||||
icon = Icons.AutoMirrored.Filled.FormatListBulleted,
|
imageVector = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||||
contentDescription = "卷管理"
|
contentDescription = "卷管理"
|
||||||
)
|
)
|
||||||
RoundDropdownMenu(
|
RoundDropdownMenu(
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ fun TxtRuleScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppAlertDialog(
|
AppAlertDialog(
|
||||||
data = showDeleteRuleDialog, // 传入 nullable 的 rule 对象
|
data = showDeleteRuleDialog, // ä¼ å…¥ nullable çš?rule 对象
|
||||||
onDismissRequest = { showDeleteRuleDialog = null },
|
onDismissRequest = { showDeleteRuleDialog = null },
|
||||||
title = stringResource(R.string.delete),
|
title = stringResource(R.string.delete),
|
||||||
confirmText = stringResource(R.string.ok),
|
confirmText = stringResource(R.string.ok),
|
||||||
@@ -228,7 +228,7 @@ fun TxtRuleScreen(
|
|||||||
editingRule = null
|
editingRule = null
|
||||||
},
|
},
|
||||||
onSave = { updatedRule ->
|
onSave = { updatedRule ->
|
||||||
//TODO:我很想把他改为自增主键,但为了兼容性日后再说
|
//TODO:我很想把他改为自增主键,但为了兼容性日���
|
||||||
if (editingRule == null) {
|
if (editingRule == null) {
|
||||||
viewModel.insert(updatedRule)
|
viewModel.insert(updatedRule)
|
||||||
} else {
|
} else {
|
||||||
@@ -346,7 +346,7 @@ fun TxtRuleScreen(
|
|||||||
trailingAction = {
|
trailingAction = {
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = { showDeleteRuleDialog = item.rule },
|
onClick = { showDeleteRuleDialog = item.rule },
|
||||||
icon = Icons.Default.Delete
|
imageVector = Icons.Default.Delete
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -368,3 +368,4 @@ fun TxtRuleScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ fun DirectLinkUploadBottomSheet(
|
|||||||
endAction = {
|
endAction = {
|
||||||
MediumIconButton(
|
MediumIconButton(
|
||||||
onClick = { showMenu = true },
|
onClick = { showMenu = true },
|
||||||
icon = Icons.Default.MoreVert
|
imageVector = Icons.Default.MoreVert
|
||||||
)
|
)
|
||||||
RoundDropdownMenu(
|
RoundDropdownMenu(
|
||||||
expanded = showMenu,
|
expanded = showMenu,
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ fun DictRuleScreen(
|
|||||||
trailingAction = {
|
trailingAction = {
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = { showDeleteRuleDialog = item.rule },
|
onClick = { showDeleteRuleDialog = item.rule },
|
||||||
icon = Icons.Default.Delete
|
imageVector = Icons.Default.Delete
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ import io.legado.app.lib.dialogs.alert
|
|||||||
import io.legado.app.service.WebService
|
import io.legado.app.service.WebService
|
||||||
import io.legado.app.ui.about.CrashLogsDialog
|
import io.legado.app.ui.about.CrashLogsDialog
|
||||||
import io.legado.app.ui.about.UpdateDialog
|
import io.legado.app.ui.about.UpdateDialog
|
||||||
|
import io.legado.app.ui.book.import.local.ImportBookScreen
|
||||||
|
import io.legado.app.ui.book.import.remote.RemoteBookScreen
|
||||||
import io.legado.app.ui.book.read.ReadBookActivity
|
import io.legado.app.ui.book.read.ReadBookActivity
|
||||||
import io.legado.app.ui.config.ConfigNavScreen
|
import io.legado.app.ui.config.ConfigNavScreen
|
||||||
import io.legado.app.ui.config.ConfigTag
|
import io.legado.app.ui.config.ConfigTag
|
||||||
@@ -74,6 +76,8 @@ open class MainActivity : BaseComposeActivity() {
|
|||||||
private const val ROUTE_SETTINGS_COVER = "settings/cover"
|
private const val ROUTE_SETTINGS_COVER = "settings/cover"
|
||||||
private const val ROUTE_SETTINGS_THEME = "settings/theme"
|
private const val ROUTE_SETTINGS_THEME = "settings/theme"
|
||||||
private const val ROUTE_SETTINGS_BACKUP = "settings/backup"
|
private const val ROUTE_SETTINGS_BACKUP = "settings/backup"
|
||||||
|
private const val ROUTE_IMPORT_LOCAL = "import/local"
|
||||||
|
private const val ROUTE_IMPORT_REMOTE = "import/remote"
|
||||||
|
|
||||||
fun createIntent(context: Context, configTag: String? = null): Intent {
|
fun createIntent(context: Context, configTag: String? = null): Intent {
|
||||||
return Intent(context, MainActivity::class.java).apply {
|
return Intent(context, MainActivity::class.java).apply {
|
||||||
@@ -120,6 +124,12 @@ open class MainActivity : BaseComposeActivity() {
|
|||||||
@Serializable
|
@Serializable
|
||||||
private data object MainRouteSettingsBackup : MainRoute
|
private data object MainRouteSettingsBackup : MainRoute
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data object MainRouteImportLocal : MainRoute
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data object MainRouteImportRemote : MainRoute
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -260,6 +270,12 @@ open class MainActivity : BaseComposeActivity() {
|
|||||||
useRail = useRail,
|
useRail = useRail,
|
||||||
onOpenSettings = {
|
onOpenSettings = {
|
||||||
navigateToRoute(backStack, MainRouteSettings)
|
navigateToRoute(backStack, MainRouteSettings)
|
||||||
|
},
|
||||||
|
onNavigateToRemoteImport = {
|
||||||
|
navigateToRoute(backStack, MainRouteImportRemote)
|
||||||
|
},
|
||||||
|
onNavigateToLocalImport = {
|
||||||
|
navigateToRoute(backStack, MainRouteImportLocal)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -294,6 +310,18 @@ open class MainActivity : BaseComposeActivity() {
|
|||||||
entry<MainRouteSettingsBackup> {
|
entry<MainRouteSettingsBackup> {
|
||||||
BackupConfigScreen(onBackClick = { navigateBack(backStack) })
|
BackupConfigScreen(onBackClick = { navigateBack(backStack) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry<MainRouteImportLocal> {
|
||||||
|
ImportBookScreen(
|
||||||
|
onBackClick = { navigateBack(backStack) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<MainRouteImportRemote> {
|
||||||
|
RemoteBookScreen(
|
||||||
|
onBackClick = { navigateBack(backStack) }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -328,6 +356,17 @@ open class MainActivity : BaseComposeActivity() {
|
|||||||
backStack.add(MainRouteSettings)
|
backStack.add(MainRouteSettings)
|
||||||
backStack.add(route)
|
backStack.add(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MainRouteImportLocal,
|
||||||
|
MainRouteImportRemote -> {
|
||||||
|
if (currentRoute == MainRouteHome) {
|
||||||
|
backStack.add(route)
|
||||||
|
} else {
|
||||||
|
backStack.clear()
|
||||||
|
backStack.add(MainRouteHome)
|
||||||
|
backStack.add(route)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +501,8 @@ open class MainActivity : BaseComposeActivity() {
|
|||||||
"settings/cover" -> MainRouteSettingsCover
|
"settings/cover" -> MainRouteSettingsCover
|
||||||
"settings/theme" -> MainRouteSettingsTheme
|
"settings/theme" -> MainRouteSettingsTheme
|
||||||
"settings/backup" -> MainRouteSettingsBackup
|
"settings/backup" -> MainRouteSettingsBackup
|
||||||
|
"import/local" -> MainRouteImportLocal
|
||||||
|
"import/remote" -> MainRouteImportRemote
|
||||||
else -> MainRouteHome
|
else -> MainRouteHome
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
fun MainScreen(
|
fun MainScreen(
|
||||||
viewModel: MainViewModel = koinViewModel(),
|
viewModel: MainViewModel = koinViewModel(),
|
||||||
useRail: Boolean,
|
useRail: Boolean,
|
||||||
onOpenSettings: () -> Unit
|
onOpenSettings: () -> Unit,
|
||||||
|
onNavigateToRemoteImport: () -> Unit,
|
||||||
|
onNavigateToLocalImport: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
@@ -373,7 +375,9 @@ fun MainScreen(
|
|||||||
putExtra("author", book.author)
|
putExtra("author", book.author)
|
||||||
putExtra("bookUrl", book.bookUrl)
|
putExtra("bookUrl", book.bookUrl)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onNavigateToRemoteImport = onNavigateToRemoteImport,
|
||||||
|
onNavigateToLocalImport = onNavigateToLocalImport
|
||||||
)
|
)
|
||||||
|
|
||||||
MainDestination.Explore -> ExploreScreen()
|
MainDestination.Explore -> ExploreScreen()
|
||||||
|
|||||||
@@ -67,12 +67,11 @@ import io.legado.app.R
|
|||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.ui.about.AppLogSheet
|
import io.legado.app.ui.about.AppLogSheet
|
||||||
import io.legado.app.ui.book.cache.CacheActivity
|
import io.legado.app.ui.book.cache.CacheActivity
|
||||||
import io.legado.app.ui.book.import.local.ImportBookActivity
|
|
||||||
import io.legado.app.ui.book.import.remote.RemoteBookActivity
|
|
||||||
import io.legado.app.ui.book.manage.BookshelfManageActivity
|
import io.legado.app.ui.book.manage.BookshelfManageActivity
|
||||||
import io.legado.app.ui.book.search.SearchActivity
|
import io.legado.app.ui.book.search.SearchActivity
|
||||||
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
import io.legado.app.ui.theme.adaptiveContentPadding
|
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||||
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
||||||
@@ -100,7 +99,9 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
fun BookshelfScreen(
|
fun BookshelfScreen(
|
||||||
viewModel: BookshelfViewModel = koinViewModel(),
|
viewModel: BookshelfViewModel = koinViewModel(),
|
||||||
onBookClick: (BookShelfItem) -> Unit,
|
onBookClick: (BookShelfItem) -> Unit,
|
||||||
onBookLongClick: (BookShelfItem) -> Unit
|
onBookLongClick: (BookShelfItem) -> Unit,
|
||||||
|
onNavigateToRemoteImport: () -> Unit,
|
||||||
|
onNavigateToLocalImport: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
@@ -196,7 +197,7 @@ fun BookshelfScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val bookGroupStyle = BookshelfConfig.bookGroupStyle
|
val bookGroupStyle = BookshelfConfig.bookGroupStyle
|
||||||
// 控制是否处于“文件夹列表”根视图,还是“文件夹内部”书籍视图
|
// 控制是å�¦å¤„于“文件夹列表â€�æ ¹è§†å›¾ï¼Œè¿˜æ˜¯â€œæ–‡ä»¶å¤¹å†…éƒ¨â€�书ç±�视å›?
|
||||||
var isInFolderRoot by remember(bookGroupStyle) { mutableStateOf(bookGroupStyle == 2) }
|
var isInFolderRoot by remember(bookGroupStyle) { mutableStateOf(bookGroupStyle == 2) }
|
||||||
val baseTitle = when {
|
val baseTitle = when {
|
||||||
bookGroupStyle == 1 -> {
|
bookGroupStyle == 1 -> {
|
||||||
@@ -232,6 +233,10 @@ fun BookshelfScreen(
|
|||||||
if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait
|
if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait
|
||||||
val bookshelfLayoutList =
|
val bookshelfLayoutList =
|
||||||
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
|
if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
|
||||||
|
val totalHorizontalPadding =
|
||||||
|
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp
|
||||||
|
val gridContentHorizontalPadding = totalHorizontalPadding / 2
|
||||||
|
val gridInnerHorizontalPadding = totalHorizontalPadding / 2
|
||||||
|
|
||||||
ListScaffold(
|
ListScaffold(
|
||||||
title = title,
|
title = title,
|
||||||
@@ -242,12 +247,12 @@ fun BookshelfScreen(
|
|||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = stringResource(R.string.add_remote_book),
|
text = stringResource(R.string.add_remote_book),
|
||||||
onClick = { context.startActivity<RemoteBookActivity>(); dismiss() },
|
onClick = { onNavigateToRemoteImport(); dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Wifi, null) }
|
leadingIcon = { Icon(Icons.Default.Wifi, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = stringResource(R.string.book_local),
|
text = stringResource(R.string.book_local),
|
||||||
onClick = { context.startActivity<ImportBookActivity>(); dismiss() },
|
onClick = { onNavigateToLocalImport(); dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Save, null) }
|
leadingIcon = { Icon(Icons.Default.Save, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
@@ -345,7 +350,7 @@ fun BookshelfScreen(
|
|||||||
SmallOutlinedIconToggleButton(
|
SmallOutlinedIconToggleButton(
|
||||||
checked = showGroupMenu,
|
checked = showGroupMenu,
|
||||||
onCheckedChange = { showGroupMenu = it },
|
onCheckedChange = { showGroupMenu = it },
|
||||||
icon = Icons.AutoMirrored.Filled.FormatListBulleted,
|
imageVector = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||||
contentDescription = stringResource(R.string.group_manage)
|
contentDescription = stringResource(R.string.group_manage)
|
||||||
)
|
)
|
||||||
RoundDropdownMenu(
|
RoundDropdownMenu(
|
||||||
@@ -407,15 +412,19 @@ fun BookshelfScreen(
|
|||||||
if (bookGroupStyle == 2 && isRoot) {
|
if (bookGroupStyle == 2 && isRoot) {
|
||||||
val folderColumns =
|
val folderColumns =
|
||||||
if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid
|
if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid
|
||||||
|
val isGridMode = bookshelfLayoutMode != 0
|
||||||
FastScrollLazyVerticalGrid(
|
FastScrollLazyVerticalGrid(
|
||||||
columns = GridCells.Fixed(folderColumns.coerceAtLeast(1)),
|
columns = GridCells.Fixed(folderColumns.coerceAtLeast(1)),
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = if (isGridMode) gridInnerHorizontalPadding else 0.dp),
|
||||||
contentPadding = adaptiveContentPadding(
|
contentPadding = adaptiveContentPadding(
|
||||||
top = paddingValues.calculateTopPadding(),
|
top = paddingValues.calculateTopPadding(),
|
||||||
bottom = 120.dp
|
bottom = 120.dp,
|
||||||
|
horizontal = if (isGridMode) gridContentHorizontalPadding else 0.dp
|
||||||
),
|
),
|
||||||
verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
|
verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
|
horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
|
||||||
showFastScroll = BookshelfConfig.showBookshelfFastScroller
|
showFastScroll = BookshelfConfig.showBookshelfFastScroller
|
||||||
) {
|
) {
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
@@ -592,16 +601,23 @@ fun BookshelfPage(
|
|||||||
onBookLongClick: (BookShelfItem) -> Unit
|
onBookLongClick: (BookShelfItem) -> Unit
|
||||||
) {
|
) {
|
||||||
val columns = if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid
|
val columns = if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid
|
||||||
|
val isGridMode = bookshelfLayoutMode != 0
|
||||||
|
val totalHorizontalPadding =
|
||||||
|
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) 12.dp else 16.dp
|
||||||
|
val gridContentHorizontalPadding = totalHorizontalPadding / 2
|
||||||
|
val gridInnerHorizontalPadding = totalHorizontalPadding / 2
|
||||||
FastScrollLazyVerticalGrid(
|
FastScrollLazyVerticalGrid(
|
||||||
columns = GridCells.Fixed(columns.coerceAtLeast(1)),
|
columns = GridCells.Fixed(columns.coerceAtLeast(1)),
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = if (isGridMode) gridInnerHorizontalPadding else 0.dp),
|
||||||
contentPadding = adaptiveContentPadding(
|
contentPadding = adaptiveContentPadding(
|
||||||
top = paddingValues.calculateTopPadding(),
|
top = paddingValues.calculateTopPadding(),
|
||||||
bottom = 120.dp,
|
bottom = 120.dp,
|
||||||
horizontal = if (bookshelfLayoutMode != 0) 12.dp else 0.dp
|
horizontal = if (isGridMode) gridContentHorizontalPadding else 0.dp
|
||||||
),
|
),
|
||||||
verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
|
verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
|
horizontalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
|
||||||
showFastScroll = BookshelfConfig.showBookshelfFastScroller
|
showFastScroll = BookshelfConfig.showBookshelfFastScroller
|
||||||
) {
|
) {
|
||||||
items(books, key = { it.bookUrl }) { book ->
|
items(books, key = { it.bookUrl }) { book ->
|
||||||
@@ -621,3 +637,4 @@ fun BookshelfPage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ fun GroupManageSheet(
|
|||||||
editingGroup = null
|
editingGroup = null
|
||||||
isEditing = true
|
isEditing = true
|
||||||
},
|
},
|
||||||
icon = Icons.Default.Add
|
imageVector = Icons.Default.Add
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,3 +130,4 @@ fun GroupManageSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -249,8 +249,11 @@ fun ExploreScreen(
|
|||||||
TextCard(
|
TextCard(
|
||||||
text = item.bookSourceName,
|
text = item.bookSourceName,
|
||||||
textStyle = LegadoTheme.typography.labelLarge,
|
textStyle = LegadoTheme.typography.labelLarge,
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.cardContainer,
|
||||||
|
contentColor = LegadoTheme.colorScheme.onCardContainer,
|
||||||
|
cornerRadius = 8.dp,
|
||||||
horizontalPadding = 8.dp,
|
horizontalPadding = 8.dp,
|
||||||
verticalPadding = 6.dp,
|
verticalPadding = 8.dp,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val index =
|
val index =
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ fun WebServiceSettingBlock(
|
|||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "复制地址",
|
text = "复制地址",
|
||||||
icon = Icons.Default.ContentCopy,
|
imageVector = Icons.Default.ContentCopy,
|
||||||
onClick = {
|
onClick = {
|
||||||
onNavigate(PrefClickEvent.CopyUrl(uiState.webServiceAddress))
|
onNavigate(PrefClickEvent.CopyUrl(uiState.webServiceAddress))
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ fun WebServiceSettingBlock(
|
|||||||
|
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "浏览器打开",
|
text = "浏览器打开",
|
||||||
icon = Icons.Default.OpenInBrowser,
|
imageVector = Icons.Default.OpenInBrowser,
|
||||||
onClick = {
|
onClick = {
|
||||||
onNavigate(PrefClickEvent.OpenUrl(uiState.webServiceAddress))
|
onNavigate(PrefClickEvent.OpenUrl(uiState.webServiceAddress))
|
||||||
}
|
}
|
||||||
@@ -254,3 +254,4 @@ fun WebServiceSettingBlock(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,9 @@ class ReplaceRuleActivity : BaseComposeActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entry<ReplaceEditRoute> { route ->
|
entry<ReplaceEditRoute> { route ->
|
||||||
val viewModel: ReplaceEditViewModel = koinViewModel { parametersOf(route) }
|
val viewModel: ReplaceEditViewModel = koinViewModel(
|
||||||
|
key = "replace_edit_${System.identityHashCode(route)}"
|
||||||
|
) { parametersOf(route) }
|
||||||
|
|
||||||
ReplaceEditScreen(
|
ReplaceEditScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
|
|||||||
@@ -42,13 +42,12 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|||||||
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
import androidx.compose.material3.FilterChip
|
|
||||||
import androidx.compose.material3.FilterChipDefaults
|
|
||||||
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
import io.legado.app.ui.widget.components.AppFloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.animateFloatingActionButton
|
import androidx.compose.material3.animateFloatingActionButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -73,8 +72,11 @@ import io.legado.app.ui.widget.components.AppScaffold
|
|||||||
import io.legado.app.ui.widget.components.AppTextField
|
import io.legado.app.ui.widget.components.AppTextField
|
||||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||||
import io.legado.app.ui.widget.components.button.AlertButton
|
import io.legado.app.ui.widget.components.button.AlertButton
|
||||||
|
import io.legado.app.ui.widget.components.button.ToggleChip
|
||||||
|
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||||
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||||
import io.legado.app.ui.widget.components.checkBox.CheckboxItem
|
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.RoundDropdownMenu
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
@@ -115,15 +117,19 @@ fun ReplaceEditScreen(
|
|||||||
enter = fadeIn(),
|
enter = fadeIn(),
|
||||||
exit = fadeOut()
|
exit = fadeOut()
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = {
|
TopBarActionButton(
|
||||||
|
onClick = {
|
||||||
viewModel.save(onSaveSuccess)
|
viewModel.save(onSaveSuccess)
|
||||||
}) {
|
},
|
||||||
Icon(Icons.Default.Save, contentDescription = "保存")
|
imageVector = Icons.Default.Save,
|
||||||
}
|
contentDescription = "保存"
|
||||||
}
|
)
|
||||||
IconButton(onClick = { showMenu = true }) {
|
|
||||||
Icon(Icons.Default.MoreVert, contentDescription = "更多操作")
|
|
||||||
}
|
}
|
||||||
|
TopBarActionButton(
|
||||||
|
onClick = { showMenu = true },
|
||||||
|
imageVector = Icons.Default.MoreVert,
|
||||||
|
contentDescription = "更多操作"
|
||||||
|
)
|
||||||
RoundDropdownMenu(
|
RoundDropdownMenu(
|
||||||
expanded = showMenu,
|
expanded = showMenu,
|
||||||
onDismissRequest = { showMenu = false }
|
onDismissRequest = { showMenu = false }
|
||||||
@@ -158,7 +164,7 @@ fun ReplaceEditScreen(
|
|||||||
onClick = { viewModel.save(onSaveSuccess) },
|
onClick = { viewModel.save(onSaveSuccess) },
|
||||||
tooltipText = "添加"
|
tooltipText = "添加"
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Default.Save, contentDescription = "保存")
|
AppIcon(Icons.Default.Save, contentDescription = "保存")
|
||||||
}
|
}
|
||||||
}, contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
}, contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
@@ -242,53 +248,29 @@ fun ReplaceEditScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Start
|
horizontalArrangement = Arrangement.Start
|
||||||
) {
|
) {
|
||||||
FilterChip(
|
ToggleChip(
|
||||||
|
label = "标题",
|
||||||
selected = state.scopeTitle,
|
selected = state.scopeTitle,
|
||||||
onClick = { viewModel.onScopeTitleChange(!state.scopeTitle) },
|
checkedContentDescription = "已选择",
|
||||||
label = { AppText("标题") },
|
onToggle = { viewModel.onScopeTitleChange(!state.scopeTitle) }
|
||||||
leadingIcon = if (state.scopeTitle) {
|
|
||||||
{
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Check,
|
|
||||||
contentDescription = "已选择",
|
|
||||||
Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
|
|
||||||
FilterChip(
|
ToggleChip(
|
||||||
|
label = "内容",
|
||||||
selected = state.scopeContent,
|
selected = state.scopeContent,
|
||||||
onClick = { viewModel.onScopeContentChange(!state.scopeContent) },
|
checkedContentDescription = "已选择",
|
||||||
label = { AppText("内容") },
|
onToggle = { viewModel.onScopeContentChange(!state.scopeContent) }
|
||||||
leadingIcon = if (state.scopeContent) {
|
|
||||||
{
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Check,
|
|
||||||
contentDescription = "已选择",
|
|
||||||
Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
FilterChip(
|
ToggleChip(
|
||||||
|
label = "使用正则",
|
||||||
selected = state.isRegex,
|
selected = state.isRegex,
|
||||||
onClick = { viewModel.onRegexChange(!state.isRegex) },
|
checkedContentDescription = "正则已启用",
|
||||||
label = { AppText("使用正则") },
|
onToggle = { viewModel.onRegexChange(!state.isRegex) }
|
||||||
leadingIcon = if (state.isRegex) {
|
|
||||||
{
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Check,
|
|
||||||
contentDescription = "正则已启用",
|
|
||||||
Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ fun RssFavoritesScreen(
|
|||||||
SelectionItemCard(
|
SelectionItemCard(
|
||||||
title = rssStar.title,
|
title = rssStar.title,
|
||||||
subtitle = if (rssStar.group.isNotBlank()) {
|
subtitle = if (rssStar.group.isNotBlank()) {
|
||||||
"${rssStar.group} • ${rssStar.pubDate ?: ""}"
|
"${rssStar.group} �${rssStar.pubDate ?: ""}"
|
||||||
} else {
|
} else {
|
||||||
rssStar.pubDate
|
rssStar.pubDate
|
||||||
},
|
},
|
||||||
@@ -232,7 +232,7 @@ fun RssFavoritesScreen(
|
|||||||
}
|
}
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = openAction,
|
onClick = openAction,
|
||||||
icon = Icons.AutoMirrored.Filled.OpenInNew,
|
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||||
contentDescription = "Open"
|
contentDescription = "Open"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -258,3 +258,4 @@ fun RssFavoritesScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ fun RssSourceScreen(
|
|||||||
trailingAction = {
|
trailingAction = {
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = { showDeleteRuleDialog = item.source },
|
onClick = { showDeleteRuleDialog = item.source },
|
||||||
icon = Icons.Default.Delete
|
imageVector = Icons.Default.Delete
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -387,3 +387,4 @@ fun RssSourceScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ fun adaptiveContentPadding(
|
|||||||
bottom: Dp,
|
bottom: Dp,
|
||||||
horizontal: Dp
|
horizontal: Dp
|
||||||
): PaddingValues {
|
): PaddingValues {
|
||||||
val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top else top + 4.dp
|
val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top + 6.dp else top + 4.dp
|
||||||
return PaddingValues(
|
return PaddingValues(
|
||||||
top = adjustedTop,
|
top = adjustedTop,
|
||||||
bottom = bottom,
|
bottom = bottom,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
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
|
||||||
|
import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
||||||
import io.legado.app.ui.widget.components.card.GlassCard
|
import io.legado.app.ui.widget.components.card.GlassCard
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ fun CollapsibleHeader(
|
|||||||
GlassCard(
|
GlassCard(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 16.dp, top = 4.dp, end = 16.dp),
|
.adaptiveHorizontalPadding(vertical = 4.dp),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||||
onClick = onToggle
|
onClick = onToggle
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.state.ToggleableState
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import top.yukonga.miuix.kmp.basic.Checkbox
|
||||||
|
import top.yukonga.miuix.kmp.basic.RadioButton as MiuixRadioButton
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AppRadioButton(
|
||||||
|
selected: Boolean,
|
||||||
|
onClick: (() -> Unit)?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
MiuixRadioButton(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
RadioButton(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@@ -120,7 +120,7 @@ private fun GroupItem(
|
|||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = stringResource(id = R.string.ok),
|
text = stringResource(id = R.string.ok),
|
||||||
icon = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
onClick = {
|
onClick = {
|
||||||
onUpdateGroup(group, state.text.toString())
|
onUpdateGroup(group, state.text.toString())
|
||||||
expanded = false
|
expanded = false
|
||||||
@@ -130,3 +130,4 @@ private fun GroupItem(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -31,7 +31,7 @@ fun ValueStepper(
|
|||||||
val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
|
val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
|
||||||
onValueChange(newValue)
|
onValueChange(newValue)
|
||||||
},
|
},
|
||||||
icon = Icons.Default.Remove
|
imageVector = Icons.Default.Remove
|
||||||
)
|
)
|
||||||
TextCard(
|
TextCard(
|
||||||
cornerRadius = 8.dp,
|
cornerRadius = 8.dp,
|
||||||
@@ -46,7 +46,7 @@ fun ValueStepper(
|
|||||||
val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
|
val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
|
||||||
onValueChange(newValue)
|
onValueChange(newValue)
|
||||||
},
|
},
|
||||||
icon = Icons.Default.Add
|
imageVector = Icons.Default.Add
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
|||||||
@Composable
|
@Composable
|
||||||
fun MediumIconButton(
|
fun MediumIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
@@ -51,7 +51,7 @@ fun MediumIconButton(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ fun MediumIconButton(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ fun MediumIconButton(
|
|||||||
@Composable
|
@Composable
|
||||||
fun MediumOutlinedIconButton(
|
fun MediumOutlinedIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
@@ -83,7 +83,7 @@ fun MediumOutlinedIconButton(
|
|||||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainerHigh
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainerHigh
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ fun MediumOutlinedIconButton(
|
|||||||
border = ButtonDefaults.outlinedButtonBorder()
|
border = ButtonDefaults.outlinedButtonBorder()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ fun MediumOutlinedIconButton(
|
|||||||
@Composable
|
@Composable
|
||||||
fun MediumOutlinedButton(
|
fun MediumOutlinedButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
text: String,
|
text: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
@@ -120,7 +120,7 @@ fun MediumOutlinedButton(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
MiuixText(text = text)
|
MiuixText(text = text)
|
||||||
@@ -136,7 +136,7 @@ fun MediumOutlinedButton(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
Text(text = text)
|
Text(text = text)
|
||||||
@@ -148,7 +148,7 @@ fun MediumOutlinedButton(
|
|||||||
@Composable
|
@Composable
|
||||||
fun MediumTonalIconButton(
|
fun MediumTonalIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
@@ -161,7 +161,7 @@ fun MediumTonalIconButton(
|
|||||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ fun MediumTonalIconButton(
|
|||||||
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ fun MediumTonalIconButton(
|
|||||||
fun MediumOutlinedIconToggleButton(
|
fun MediumOutlinedIconToggleButton(
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
@@ -208,7 +208,7 @@ fun MediumOutlinedIconToggleButton(
|
|||||||
backgroundColor = containerColor
|
backgroundColor = containerColor
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
tint = iconTint
|
tint = iconTint
|
||||||
)
|
)
|
||||||
@@ -220,7 +220,7 @@ fun MediumOutlinedIconToggleButton(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ private fun SmallNoMinTouchTarget(content: @Composable () -> Unit) {
|
|||||||
@Composable
|
@Composable
|
||||||
fun SmallIconButton(
|
fun SmallIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
@@ -72,7 +72,7 @@ fun SmallIconButton(
|
|||||||
onClick = onClick
|
onClick = onClick
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ fun SmallIconButton(
|
|||||||
shape = IconButtonDefaults.extraSmallRoundShape,
|
shape = IconButtonDefaults.extraSmallRoundShape,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(smallIconSize),
|
modifier = Modifier.size(smallIconSize),
|
||||||
)
|
)
|
||||||
@@ -97,11 +97,10 @@ fun SmallIconButton(
|
|||||||
@Composable
|
@Composable
|
||||||
fun SmallOutlinedIconButton(
|
fun SmallOutlinedIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
||||||
if (isMiuix) {
|
if (isMiuix) {
|
||||||
MiuixIconButton(
|
MiuixIconButton(
|
||||||
@@ -110,7 +109,7 @@ fun SmallOutlinedIconButton(
|
|||||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(SmallMiuixIconSize)
|
modifier = Modifier.size(SmallMiuixIconSize)
|
||||||
)
|
)
|
||||||
@@ -124,7 +123,7 @@ fun SmallOutlinedIconButton(
|
|||||||
border = ButtonDefaults.outlinedButtonBorder()
|
border = ButtonDefaults.outlinedButtonBorder()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(smallIconSize)
|
modifier = Modifier.size(smallIconSize)
|
||||||
)
|
)
|
||||||
@@ -137,7 +136,7 @@ fun SmallOutlinedIconButton(
|
|||||||
@Composable
|
@Composable
|
||||||
fun SmallTonalIconButton(
|
fun SmallTonalIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
@@ -150,7 +149,7 @@ fun SmallTonalIconButton(
|
|||||||
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(SmallMiuixIconSize)
|
modifier = Modifier.size(SmallMiuixIconSize)
|
||||||
)
|
)
|
||||||
@@ -164,7 +163,7 @@ fun SmallTonalIconButton(
|
|||||||
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(smallIconSize)
|
modifier = Modifier.size(smallIconSize)
|
||||||
)
|
)
|
||||||
@@ -178,7 +177,7 @@ fun SmallTonalIconButton(
|
|||||||
fun SmallOutlinedIconToggleButton(
|
fun SmallOutlinedIconToggleButton(
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
@@ -205,7 +204,7 @@ fun SmallOutlinedIconToggleButton(
|
|||||||
backgroundColor = containerColor
|
backgroundColor = containerColor
|
||||||
) {
|
) {
|
||||||
MiuixIcon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
tint = iconTint,
|
tint = iconTint,
|
||||||
modifier = Modifier.size(SmallMiuixIconSize)
|
modifier = Modifier.size(SmallMiuixIconSize)
|
||||||
@@ -233,7 +232,7 @@ fun SmallOutlinedIconToggleButton(
|
|||||||
shapes = toggleShapes
|
shapes = toggleShapes
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(smallIconSize),
|
modifier = Modifier.size(smallIconSize),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
|||||||
@Composable
|
@Composable
|
||||||
fun SmallTextButton(
|
fun SmallTextButton(
|
||||||
text: String,
|
text: String,
|
||||||
icon: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -44,7 +44,7 @@ fun SmallTextButton(
|
|||||||
shape = MaterialTheme.shapes.small
|
shape = MaterialTheme.shapes.small
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = imageVector,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(16.dp)
|
modifier = Modifier.size(16.dp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package io.legado.app.ui.widget.components.button
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.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
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToggleChip(
|
||||||
|
label: String,
|
||||||
|
selected: Boolean,
|
||||||
|
onToggle: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
checkedContentDescription: String = "已选择"
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
MiuixIconButton(
|
||||||
|
onClick = onToggle,
|
||||||
|
modifier = modifier,
|
||||||
|
backgroundColor = if (selected) {
|
||||||
|
MiuixTheme.colorScheme.primaryContainer
|
||||||
|
} else {
|
||||||
|
MiuixTheme.colorScheme.surfaceContainer
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(visible = selected) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
MiuixIcon(
|
||||||
|
imageVector = Icons.Default.Check,
|
||||||
|
contentDescription = checkedContentDescription,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(6.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MiuixText(
|
||||||
|
text = label,
|
||||||
|
style = LegadoTheme.typography.labelSmall,
|
||||||
|
maxLines = 1,
|
||||||
|
softWrap = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FilterChip(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onToggle,
|
||||||
|
modifier = modifier,
|
||||||
|
label = { Text(label) },
|
||||||
|
leadingIcon = if (selected) {
|
||||||
|
{
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Check,
|
||||||
|
contentDescription = checkedContentDescription,
|
||||||
|
Modifier.size(FilterChipDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -181,7 +181,7 @@ fun SelectionItemCard(
|
|||||||
if (onClickEdit != null) {
|
if (onClickEdit != null) {
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = onClickEdit,
|
onClick = onClickEdit,
|
||||||
icon = Icons.Default.Edit,
|
imageVector = Icons.Default.Edit,
|
||||||
contentDescription = "Edit"
|
contentDescription = "Edit"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ fun SelectionItemCard(
|
|||||||
Box {
|
Box {
|
||||||
SmallIconButton(
|
SmallIconButton(
|
||||||
onClick = { showMenu = true },
|
onClick = { showMenu = true },
|
||||||
icon = Icons.Default.MoreVert,
|
imageVector = Icons.Default.MoreVert,
|
||||||
contentDescription = "More"
|
contentDescription = "More"
|
||||||
)
|
)
|
||||||
RoundDropdownMenu(
|
RoundDropdownMenu(
|
||||||
@@ -272,3 +272,4 @@ fun LazyItemScope.ReorderableSelectionItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-19
@@ -19,7 +19,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.AccessTime
|
import androidx.compose.material.icons.filled.AccessTime
|
||||||
import androidx.compose.material.icons.filled.FormatListNumbered
|
import androidx.compose.material.icons.filled.FormatListNumbered
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -43,24 +42,18 @@ import io.legado.app.ui.widget.components.text.AppText
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 热力图日历顶部操作栏
|
* 热力图日历弹窗标题
|
||||||
|
*/
|
||||||
|
const val HEATMAP_CALENDAR_TITLE = "时间线"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 热力图日历弹窗左侧操作
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HeatmapCalendarTopBar(
|
fun HeatmapCalendarStartAction(
|
||||||
currentMode: HeatmapMode,
|
currentMode: HeatmapMode,
|
||||||
onModeChanged: (HeatmapMode) -> Unit,
|
onModeChanged: (HeatmapMode) -> Unit,
|
||||||
onClearDate: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
) {
|
||||||
Row(
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
AppText("时间线", style = LegadoTheme.typography.titleMedium)
|
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
MediumAnimatedActionButton(
|
MediumAnimatedActionButton(
|
||||||
checked = currentMode == HeatmapMode.TIME,
|
checked = currentMode == HeatmapMode.TIME,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
@@ -71,16 +64,20 @@ fun HeatmapCalendarTopBar(
|
|||||||
activeText = "按时长",
|
activeText = "按时长",
|
||||||
inactiveText = "按次数"
|
inactiveText = "按次数"
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
/**
|
||||||
|
* 热力图日历弹窗右侧操作
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun HeatmapCalendarEndAction(
|
||||||
|
onClearDate: () -> Unit
|
||||||
|
) {
|
||||||
MediumOutlinedIconButton(
|
MediumOutlinedIconButton(
|
||||||
onClick = onClearDate,
|
onClick = onClearDate,
|
||||||
icon = Icons.Outlined.Delete,
|
imageVector = Icons.Outlined.Delete,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 星期标签列(周一到周日)
|
* 星期标签列(周一到周日)
|
||||||
|
|||||||
+3
-4
@@ -41,7 +41,6 @@ fun AppModalBottomSheet(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
startAction: @Composable (() -> Unit)? = null,
|
startAction: @Composable (() -> Unit)? = null,
|
||||||
endAction: @Composable (() -> Unit)? = null,
|
endAction: @Composable (() -> Unit)? = null,
|
||||||
containerColor: Color = LegadoTheme.colorScheme.surfaceContainer,
|
|
||||||
content: @Composable ColumnScope.() -> Unit
|
content: @Composable ColumnScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
||||||
@@ -52,14 +51,15 @@ fun AppModalBottomSheet(
|
|||||||
startAction = startAction,
|
startAction = startAction,
|
||||||
endAction = endAction,
|
endAction = endAction,
|
||||||
insideMargin = DpSize(16.dp, 12.dp),
|
insideMargin = DpSize(16.dp, 12.dp),
|
||||||
backgroundColor = containerColor,
|
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onDismissFinished = onDismissRequest,
|
onDismissFinished = onDismissRequest,
|
||||||
enableWindowDim = true,
|
enableWindowDim = true,
|
||||||
allowDismiss = true
|
allowDismiss = true
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.animateContentSize(),
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,6 @@ fun AppModalBottomSheet(
|
|||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
containerColor = containerColor,
|
|
||||||
contentColor = colorScheme.onSurface,
|
contentColor = colorScheme.onSurface,
|
||||||
dragHandle = { BottomSheetDefaults.DragHandle() }
|
dragHandle = { BottomSheetDefaults.DragHandle() }
|
||||||
) {
|
) {
|
||||||
|
|||||||
+1
-2
@@ -32,8 +32,7 @@ fun OptionSheet(
|
|||||||
AppModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
show = show,
|
show = show,
|
||||||
title = title,
|
title = title,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest
|
||||||
containerColor = LegadoTheme.colorScheme.surface
|
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -34,15 +34,19 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
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.AppFloatingActionButton
|
||||||
import io.legado.app.ui.widget.components.AppTextField
|
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.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.modalBottomSheet.AppModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用编辑数据包装,用于适配不同的规则实体
|
* 通用编辑数据包装,用于适配不同的规则
|
||||||
*/
|
*/
|
||||||
data class RuleEditFields(
|
data class RuleEditFields(
|
||||||
val name: String = "",
|
val name: String = "",
|
||||||
@@ -80,28 +84,30 @@ fun <T> RuleEditSheet(
|
|||||||
AppModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
title = title,
|
title = title,
|
||||||
startAction = {
|
startAction = {
|
||||||
IconButton(onClick = onDismissRequest) {
|
MediumIconButton(
|
||||||
Icon(
|
onClick = onDismissRequest,
|
||||||
Icons.Default.Close,
|
imageVector = Icons.Default.Close,
|
||||||
contentDescription = stringResource(R.string.cancel)
|
contentDescription = "Close",
|
||||||
)
|
)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
endAction = {
|
endAction = {
|
||||||
IconButton(onClick = { showMenu = true }) {
|
Box{
|
||||||
Icon(Icons.Default.MoreVert, contentDescription = "More")
|
MediumIconButton(
|
||||||
}
|
onClick = { showMenu = true },
|
||||||
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
imageVector = Icons.Default.MoreVert,
|
||||||
DropdownMenuItem(
|
contentDescription = "More"
|
||||||
text = { AppText(stringResource(R.string.copy_rule)) },
|
)
|
||||||
|
RoundDropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.copy_rule),
|
||||||
leadingIcon = { Icon(Icons.AutoMirrored.Filled.NoteAdd, null) },
|
leadingIcon = { Icon(Icons.AutoMirrored.Filled.NoteAdd, null) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onCopy(getCurrentEntity())
|
onCopy(getCurrentEntity())
|
||||||
showMenu = false
|
showMenu = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.paste_rule)) },
|
text = stringResource(R.string.paste_rule),
|
||||||
leadingIcon = { Icon(Icons.Default.ContentPaste, null) },
|
leadingIcon = { Icon(Icons.Default.ContentPaste, null) },
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -116,6 +122,7 @@ fun <T> RuleEditSheet(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
show = show,
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
@@ -132,6 +139,7 @@ fun <T> RuleEditSheet(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = name,
|
value = name,
|
||||||
onValueChange = { name = it },
|
onValueChange = { name = it },
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.surface,
|
||||||
label = stringResource(R.string.name),
|
label = stringResource(R.string.name),
|
||||||
singleLine = true
|
singleLine = true
|
||||||
)
|
)
|
||||||
@@ -139,12 +147,14 @@ fun <T> RuleEditSheet(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = rule1,
|
value = rule1,
|
||||||
onValueChange = { rule1 = it },
|
onValueChange = { rule1 = it },
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.surface,
|
||||||
label = label1
|
label = label1
|
||||||
)
|
)
|
||||||
AppTextField(
|
AppTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = rule2,
|
value = rule2,
|
||||||
onValueChange = { rule2 = it },
|
onValueChange = { rule2 = it },
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.surface,
|
||||||
label = label2,
|
label = label2,
|
||||||
minLines = 3
|
minLines = 3
|
||||||
)
|
)
|
||||||
|
|||||||
+4
-4
@@ -84,14 +84,14 @@ fun InputSettingItem(
|
|||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "默认",
|
text = "默认",
|
||||||
icon = Icons.Default.Replay,
|
imageVector = Icons.Default.Replay,
|
||||||
onClick = {
|
onClick = {
|
||||||
state.edit { replace(0, length, defaultValue.toString()) }
|
state.edit { replace(0, length, defaultValue.toString()) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "确认",
|
text = "确认",
|
||||||
icon = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
onClick = {
|
onClick = {
|
||||||
onConfirm(state.text.toString())
|
onConfirm(state.text.toString())
|
||||||
expanded = false
|
expanded = false
|
||||||
@@ -133,14 +133,14 @@ fun InputSettingItem(
|
|||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "默认",
|
text = "默认",
|
||||||
icon = Icons.Default.Replay,
|
imageVector = Icons.Default.Replay,
|
||||||
onClick = {
|
onClick = {
|
||||||
state.edit { replace(0, length, defaultValue.toString()) }
|
state.edit { replace(0, length, defaultValue.toString()) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "确认",
|
text = "确认",
|
||||||
icon = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
onClick = {
|
onClick = {
|
||||||
onConfirm(state.text.toString())
|
onConfirm(state.text.toString())
|
||||||
expanded = false
|
expanded = false
|
||||||
|
|||||||
+12
-11
@@ -1,4 +1,4 @@
|
|||||||
package io.legado.app.ui.widget.components.settingItem
|
package io.legado.app.ui.widget.components.settingItem
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
@@ -78,7 +78,7 @@ fun SliderSettingItem(
|
|||||||
MiuixTextField(
|
MiuixTextField(
|
||||||
state = textFieldState,
|
state = textFieldState,
|
||||||
lineLimits = TextFieldLineLimits.SingleLine,
|
lineLimits = TextFieldLineLimits.SingleLine,
|
||||||
label = "输入数值 (${valueRange.start.toInt()}-${valueRange.endInclusive.toInt()})",
|
label = "杈撳叆鏁板€?(${valueRange.start.toInt()}-${valueRange.endInclusive.toInt()})",
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
inputTransformation = {
|
inputTransformation = {
|
||||||
@@ -120,16 +120,16 @@ fun SliderSettingItem(
|
|||||||
horizontalArrangement = Arrangement.End
|
horizontalArrangement = Arrangement.End
|
||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = if (isInputMode) "滑块" else "输入",
|
text = if (isInputMode) "婊戝潡" else "杈撳叆",
|
||||||
icon = if (isInputMode) Icons.Default.LinearScale else Icons.Default.Edit,
|
imageVector = if (isInputMode) Icons.Default.LinearScale else Icons.Default.Edit,
|
||||||
onClick = { isInputMode = !isInputMode }
|
onClick = { isInputMode = !isInputMode }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
|
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "默认",
|
text = "榛樿",
|
||||||
icon = Icons.Default.RestartAlt,
|
imageVector = Icons.Default.RestartAlt,
|
||||||
onClick = {
|
onClick = {
|
||||||
onValueChange(defaultValue)
|
onValueChange(defaultValue)
|
||||||
textFieldState.edit {
|
textFieldState.edit {
|
||||||
@@ -163,7 +163,7 @@ fun SliderSettingItem(
|
|||||||
TextField(
|
TextField(
|
||||||
state = textFieldState,
|
state = textFieldState,
|
||||||
lineLimits = TextFieldLineLimits.SingleLine,
|
lineLimits = TextFieldLineLimits.SingleLine,
|
||||||
label = { AppText("输入数值 (${valueRange.start.toInt()}-${valueRange.endInclusive.toInt()})") },
|
label = { AppText("杈撳叆鏁板€?(${valueRange.start.toInt()}-${valueRange.endInclusive.toInt()})") },
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -212,16 +212,16 @@ fun SliderSettingItem(
|
|||||||
horizontalArrangement = Arrangement.End
|
horizontalArrangement = Arrangement.End
|
||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = if (isInputMode) "滑块" else "输入",
|
text = if (isInputMode) "婊戝潡" else "杈撳叆",
|
||||||
icon = if (isInputMode) Icons.Default.LinearScale else Icons.Default.Edit,
|
imageVector = if (isInputMode) Icons.Default.LinearScale else Icons.Default.Edit,
|
||||||
onClick = { isInputMode = !isInputMode }
|
onClick = { isInputMode = !isInputMode }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
|
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
text = "默认",
|
text = "榛樿",
|
||||||
icon = Icons.Default.RestartAlt,
|
imageVector = Icons.Default.RestartAlt,
|
||||||
onClick = {
|
onClick = {
|
||||||
onValueChange(defaultValue)
|
onValueChange(defaultValue)
|
||||||
textFieldState.edit {
|
textFieldState.edit {
|
||||||
@@ -238,3 +238,4 @@ fun SliderSettingItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import androidx.compose.ui.graphics.graphicsLayer
|
|||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -128,7 +129,7 @@ private fun SwipeBackground(
|
|||||||
targetValue = if (isThresholdReached)
|
targetValue = if (isThresholdReached)
|
||||||
action.background
|
action.background
|
||||||
else
|
else
|
||||||
MaterialTheme.colorScheme.surfaceVariant,
|
LegadoTheme.colorScheme.surfaceVariant,
|
||||||
label = "bgColor"
|
label = "bgColor"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ private fun SwipeBackground(
|
|||||||
tint = if (isThresholdReached)
|
tint = if (isThresholdReached)
|
||||||
contentColorFor(action.background)
|
contentColorFor(action.background)
|
||||||
else
|
else
|
||||||
MaterialTheme.colorScheme.onSurfaceVariant
|
LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
accompanistWebview = "0.36.0"
|
accompanistWebview = "0.36.0"
|
||||||
adaptive = "1.3.0-alpha09"
|
adaptive = "1.3.0-alpha10"
|
||||||
adaptiveLayout = "1.3.0-alpha09"
|
adaptiveLayout = "1.3.0-alpha10"
|
||||||
adaptiveNavigation = "1.3.0-alpha09"
|
adaptiveNavigation = "1.3.0-alpha10"
|
||||||
animation = "1.10.6"
|
animation = "1.10.6"
|
||||||
biometric = "1.4.0-alpha06"
|
biometric = "1.4.0-alpha06"
|
||||||
coilCompose = "2.7.0"
|
coilCompose = "2.7.0"
|
||||||
@@ -46,8 +46,8 @@ liveeventbus = "1.8.14"
|
|||||||
navigation3 = "1.0.1"
|
navigation3 = "1.0.1"
|
||||||
markdownCompose = "0.5.0"
|
markdownCompose = "0.5.0"
|
||||||
markwon = "4.6.2"
|
markwon = "4.6.2"
|
||||||
material = "1.14.0-alpha10"
|
material = "1.14.0-beta01"
|
||||||
material3 = "1.5.0-alpha15"
|
material3 = "1.5.0-alpha17"
|
||||||
material3IconsExtended = "1.7.8"
|
material3IconsExtended = "1.7.8"
|
||||||
materialKolor = "4.1.1"
|
materialKolor = "4.1.1"
|
||||||
media = "1.7.1"
|
media = "1.7.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user