[优化] 增加控件
This commit is contained in:
81 files changed
+2459
-1706
No files matched your search
@@ -15,7 +15,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.FilledTonalIconButton
|
import androidx.compose.material3.FilledTonalIconButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -91,8 +90,6 @@ fun AboutScreen(
|
|||||||
.wrapContentWidth(Alignment.CenterHorizontally))
|
.wrapContentWidth(Alignment.CenterHorizontally))
|
||||||
TextCard(
|
TextCard(
|
||||||
text = versionName,
|
text = versionName,
|
||||||
backgroundColor = colorScheme.tertiaryContainer,
|
|
||||||
contentColor = colorScheme.onTertiaryContainer,
|
|
||||||
cornerRadius = 8.dp,
|
cornerRadius = 8.dp,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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 io.legado.app.utils.LogUtils
|
import io.legado.app.utils.LogUtils
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@@ -39,12 +39,14 @@ import java.util.Date
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppLogSheet(
|
fun AppLogSheet(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
var logs by remember { mutableStateOf(AppLog.logs) }
|
var logs by remember { mutableStateOf(AppLog.logs) }
|
||||||
var showStackTrace by remember { mutableStateOf<String?>(null) }
|
var showStackTrace by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
|||||||
import io.legado.app.ui.widget.components.lazylist.Scroller
|
import io.legado.app.ui.widget.components.lazylist.Scroller
|
||||||
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.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
@@ -128,7 +127,7 @@ fun AllBookmarkScreen(
|
|||||||
onDismissRequest = { showMenu = false }
|
onDismissRequest = { showMenu = false }
|
||||||
) {
|
) {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("导出 JSON") },
|
text = "导出 JSON",
|
||||||
onClick = {
|
onClick = {
|
||||||
showMenu = false
|
showMenu = false
|
||||||
pendingExportIsMd = false
|
pendingExportIsMd = false
|
||||||
@@ -136,7 +135,7 @@ fun AllBookmarkScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("导出 Markdown") },
|
text = "导出 Markdown",
|
||||||
onClick = {
|
onClick = {
|
||||||
showMenu = false
|
showMenu = false
|
||||||
pendingExportIsMd = true
|
pendingExportIsMd = true
|
||||||
@@ -238,22 +237,21 @@ fun AllBookmarkScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBottomSheet && editingBookmark != null) {
|
BookmarkEditSheet(
|
||||||
BookmarkEditSheet(
|
show = showBottomSheet && editingBookmark != null,
|
||||||
bookmark = editingBookmark!!,
|
bookmark = editingBookmark ?: Bookmark(),
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
showBottomSheet = false
|
showBottomSheet = false
|
||||||
editingBookmark = null
|
editingBookmark = null
|
||||||
},
|
},
|
||||||
onSave = { updatedBookmark ->
|
onSave = { updatedBookmark ->
|
||||||
viewModel.updateBookmark(updatedBookmark)
|
viewModel.updateBookmark(updatedBookmark)
|
||||||
showBottomSheet = false
|
showBottomSheet = false
|
||||||
},
|
},
|
||||||
onDelete = { bookmarkToDelete ->
|
onDelete = { bookmarkToDelete ->
|
||||||
viewModel.deleteBookmark(bookmarkToDelete)
|
viewModel.deleteBookmark(bookmarkToDelete)
|
||||||
showBottomSheet = false
|
showBottomSheet = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ import io.legado.app.ui.widget.components.card.TextCard
|
|||||||
import io.legado.app.ui.widget.components.cover.Cover
|
import io.legado.app.ui.widget.components.cover.Cover
|
||||||
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.modalBottomSheet.GlassModalBottomSheet
|
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 io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
@@ -180,112 +180,111 @@ fun ExploreShowScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showGridCountSheet) {
|
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = showGridCountSheet,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp),
|
||||||
|
onDismissRequest = { showGridCountSheet = false }
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(16.dp),
|
.fillMaxWidth()
|
||||||
onDismissRequest = { showGridCountSheet = false }
|
.padding(bottom = 16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Row(
|
AppText(
|
||||||
modifier = Modifier
|
text = "布局列数",
|
||||||
.fillMaxWidth()
|
style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
|
||||||
.padding(bottom = 16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.Center
|
|
||||||
) {
|
|
||||||
AppText(
|
|
||||||
text = "布局列数",
|
|
||||||
style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
|
||||||
|
|
||||||
TextCard(
|
|
||||||
text = "$gridColumnCount 列",
|
|
||||||
textStyle = LegadoTheme.typography.titleSmall,
|
|
||||||
verticalPadding = 4.dp,
|
|
||||||
horizontalPadding = 12.dp,
|
|
||||||
cornerRadius = 12.dp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Slider(
|
|
||||||
value = gridColumnCount.toFloat(),
|
|
||||||
onValueChange = {
|
|
||||||
val col = it.toInt().coerceIn(1, 10)
|
|
||||||
viewModel.saveGridCount(col)
|
|
||||||
},
|
|
||||||
valueRange = 1f..10f,
|
|
||||||
steps = 8,
|
|
||||||
modifier = Modifier.padding(horizontal = 20.dp)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
|
||||||
OutlinedButton(
|
TextCard(
|
||||||
onClick = { showGridCountSheet = false },
|
text = "$gridColumnCount 列",
|
||||||
modifier = Modifier
|
textStyle = LegadoTheme.typography.titleSmall,
|
||||||
.fillMaxWidth()
|
verticalPadding = 4.dp,
|
||||||
.padding(horizontal = 16.dp)
|
horizontalPadding = 12.dp,
|
||||||
) {
|
cornerRadius = 12.dp
|
||||||
AppText("完成")
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Slider(
|
||||||
|
value = gridColumnCount.toFloat(),
|
||||||
|
onValueChange = {
|
||||||
|
val col = it.toInt().coerceIn(1, 10)
|
||||||
|
viewModel.saveGridCount(col)
|
||||||
|
},
|
||||||
|
valueRange = 1f..10f,
|
||||||
|
steps = 8,
|
||||||
|
modifier = Modifier.padding(horizontal = 20.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(24.dp))
|
||||||
|
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { showGridCountSheet = false },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
AppText("完成")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showKindSheet) {
|
|
||||||
GlassModalBottomSheet(
|
|
||||||
onDismissRequest = { showKindSheet = false }
|
|
||||||
) {
|
|
||||||
|
|
||||||
var kindQuery by remember { mutableStateOf("") }
|
AppModalBottomSheet(
|
||||||
|
show = showKindSheet,
|
||||||
|
onDismissRequest = { showKindSheet = false }
|
||||||
|
) {
|
||||||
|
|
||||||
SearchBarSection(
|
var kindQuery by remember { mutableStateOf("") }
|
||||||
query = kindQuery,
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
|
||||||
onQueryChange = { kindQuery = it },
|
|
||||||
placeholder = "选择或搜索分类",
|
|
||||||
)
|
|
||||||
|
|
||||||
val filteredKinds = remember(kindQuery, kinds) {
|
SearchBarSection(
|
||||||
if (kindQuery.isBlank()) kinds
|
query = kindQuery,
|
||||||
else kinds.filter { kind ->
|
backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
kind.title.contains(kindQuery, ignoreCase = true) ||
|
onQueryChange = { kindQuery = it },
|
||||||
(kind.url?.contains(kindQuery, ignoreCase = true) == true)
|
placeholder = "选择或搜索分类",
|
||||||
}
|
)
|
||||||
|
|
||||||
|
val filteredKinds = remember(kindQuery, kinds) {
|
||||||
|
if (kindQuery.isBlank()) kinds
|
||||||
|
else kinds.filter { kind ->
|
||||||
|
kind.title.contains(kindQuery, ignoreCase = true) ||
|
||||||
|
(kind.url?.contains(kindQuery, ignoreCase = true) == true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
columns = GridCells.Fixed(3),
|
columns = GridCells.Fixed(3),
|
||||||
contentPadding = PaddingValues(16.dp),
|
contentPadding = PaddingValues(16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.weight(1f, fill = false)
|
modifier = Modifier.weight(1f, fill = false)
|
||||||
) {
|
) {
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
items = filteredKinds,
|
items = filteredKinds,
|
||||||
key = { index, kind -> "${kind.url ?: kind.title}_$index" },
|
key = { index, kind -> "${kind.url ?: kind.title}_$index" },
|
||||||
span = { _, kind ->
|
span = { _, kind ->
|
||||||
val isClickable = !kind.url.isNullOrBlank()
|
val isClickable = !kind.url.isNullOrBlank()
|
||||||
if (isClickable) GridItemSpan(1) else GridItemSpan(3)
|
if (isClickable) GridItemSpan(1) else GridItemSpan(3)
|
||||||
|
}
|
||||||
|
) { _, kind ->
|
||||||
|
KindGridItem(
|
||||||
|
modifier = Modifier.animateItem(),
|
||||||
|
kind = kind,
|
||||||
|
currentTitle = selectedTitle ?: title,
|
||||||
|
onClick = {
|
||||||
|
showKindSheet = false
|
||||||
|
viewModel.switchExploreUrl(kind)
|
||||||
}
|
}
|
||||||
) { _, kind ->
|
)
|
||||||
KindGridItem(
|
|
||||||
modifier = Modifier.animateItem(),
|
|
||||||
kind = kind,
|
|
||||||
currentTitle = selectedTitle ?: title,
|
|
||||||
onClick = {
|
|
||||||
showKindSheet = false
|
|
||||||
viewModel.switchExploreUrl(kind)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
@@ -339,7 +338,7 @@ fun ExploreShowScreen(
|
|||||||
onDismissRequest = { showMenu = false }
|
onDismissRequest = { showMenu = false }
|
||||||
) {
|
) {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("全部显示") },
|
text = "全部显示",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setFilterState(BookFilterState.SHOW_ALL)
|
viewModel.setFilterState(BookFilterState.SHOW_ALL)
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -351,7 +350,7 @@ fun ExploreShowScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("隐藏已在书架的同源书籍") },
|
text = "隐藏已在书架的同源书籍",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setFilterState(BookFilterState.HIDE_IN_SHELF)
|
viewModel.setFilterState(BookFilterState.HIDE_IN_SHELF)
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -363,7 +362,7 @@ fun ExploreShowScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("隐藏已在书架的非同源书籍") },
|
text = "隐藏已在书架的非同源书籍",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setFilterState(BookFilterState.HIDE_SAME_NAME_AUTHOR)
|
viewModel.setFilterState(BookFilterState.HIDE_SAME_NAME_AUTHOR)
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -375,7 +374,7 @@ fun ExploreShowScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("只显示不在书架的书籍") },
|
text = "只显示不在书架的书籍",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setFilterState(BookFilterState.SHOW_NOT_IN_SHELF_ONLY)
|
viewModel.setFilterState(BookFilterState.SHOW_NOT_IN_SHELF_ONLY)
|
||||||
showMenu = false
|
showMenu = false
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
@@ -36,9 +35,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.data.entities.BookGroup
|
import io.legado.app.data.entities.BookGroup
|
||||||
import io.legado.app.lib.dialogs.alert
|
import io.legado.app.lib.dialogs.alert
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
|
||||||
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.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
|
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
|
import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
@@ -55,11 +53,12 @@ import java.io.FileOutputStream
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupEditSheet(
|
fun GroupEditSheet(
|
||||||
|
show: Boolean,
|
||||||
group: BookGroup? = null,
|
group: BookGroup? = null,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
viewModel: GroupViewModel = koinViewModel()
|
viewModel: GroupViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
|
AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
|
||||||
GroupEditContent(
|
GroupEditContent(
|
||||||
group = group,
|
group = group,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
@@ -108,17 +107,9 @@ fun GroupEditContent(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.padding(bottom = 16.dp)
|
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
AppText(
|
|
||||||
text = stringResource(R.string.group_edit),
|
|
||||||
style = LegadoTheme.typography.titleLarge,
|
|
||||||
modifier = Modifier.padding(vertical = 16.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ import io.legado.app.ui.widget.components.button.SmallTonalIconButton
|
|||||||
import io.legado.app.ui.widget.components.card.TextCard
|
import io.legado.app.ui.widget.components.card.TextCard
|
||||||
import io.legado.app.ui.widget.components.list.ListScaffold
|
import io.legado.app.ui.widget.components.list.ListScaffold
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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 io.legado.app.utils.ArchiveUtils
|
import io.legado.app.utils.ArchiveUtils
|
||||||
import io.legado.app.utils.ConvertUtils
|
import io.legado.app.utils.ConvertUtils
|
||||||
@@ -199,42 +199,41 @@ fun RemoteBookScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSheet != null) {
|
AppModalBottomSheet(
|
||||||
GlassModalBottomSheet(
|
show = showSheet != null,
|
||||||
onDismissRequest = { showSheet = null }
|
onDismissRequest = { showSheet = null }
|
||||||
) {
|
) {
|
||||||
when (val state = showSheet) {
|
when (val state = showSheet) {
|
||||||
is RemoteBookSheet.Servers -> {
|
is RemoteBookSheet.Servers -> {
|
||||||
ServersSheetContent(
|
ServersSheetContent(
|
||||||
servers = uiState.servers,
|
servers = uiState.servers,
|
||||||
selectedServerId = uiState.selectedServerId,
|
selectedServerId = uiState.selectedServerId,
|
||||||
onSelect = {
|
onSelect = {
|
||||||
viewModel.selectServer(it)
|
viewModel.selectServer(it)
|
||||||
showSheet = null
|
showSheet = null
|
||||||
},
|
},
|
||||||
onEdit = { showSheet = RemoteBookSheet.ServerConfig(it) },
|
onEdit = { showSheet = RemoteBookSheet.ServerConfig(it) },
|
||||||
onDelete = { viewModel.deleteServer(it) },
|
onDelete = { viewModel.deleteServer(it) },
|
||||||
onAdd = { showSheet = RemoteBookSheet.ServerConfig(null) },
|
onAdd = { showSheet = RemoteBookSheet.ServerConfig(null) },
|
||||||
onDefault = {
|
onDefault = {
|
||||||
viewModel.selectServer(AppConst.DEFAULT_WEBDAV_ID)
|
viewModel.selectServer(AppConst.DEFAULT_WEBDAV_ID)
|
||||||
showSheet = null
|
showSheet = null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
is RemoteBookSheet.ServerConfig -> {
|
|
||||||
ServerConfigSheetContent(
|
|
||||||
server = state.server,
|
|
||||||
onSave = {
|
|
||||||
viewModel.saveServer(it)
|
|
||||||
showSheet = RemoteBookSheet.Servers
|
|
||||||
},
|
|
||||||
onCancel = { showSheet = RemoteBookSheet.Servers }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is RemoteBookSheet.ServerConfig -> {
|
||||||
|
ServerConfigSheetContent(
|
||||||
|
server = state.server,
|
||||||
|
onSave = {
|
||||||
|
viewModel.saveServer(it)
|
||||||
|
showSheet = RemoteBookSheet.Servers
|
||||||
|
},
|
||||||
|
onCancel = { showSheet = RemoteBookSheet.Servers }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +259,7 @@ fun RemoteBookScreen(
|
|||||||
},
|
},
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("按名称排序") },
|
text = "按名称排序",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.toggleSort(RemoteBookSort.Name)
|
viewModel.toggleSort(RemoteBookSort.Name)
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -272,7 +271,7 @@ fun RemoteBookScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("按时间排序") },
|
text = "按时间排序",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.toggleSort(RemoteBookSort.Default)
|
viewModel.toggleSort(RemoteBookSort.Default)
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|||||||
@@ -32,13 +32,9 @@ import androidx.compose.material.icons.filled.Schedule
|
|||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Timeline
|
import androidx.compose.material.icons.filled.Timeline
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
@@ -78,13 +74,14 @@ 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.widget.components.AnimatedTextLine
|
import io.legado.app.ui.widget.CollapsibleHeader
|
||||||
import io.legado.app.ui.widget.components.AppScaffold
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||||
import io.legado.app.ui.widget.components.SearchBarSection
|
import io.legado.app.ui.widget.components.SearchBarSection
|
||||||
import io.legado.app.ui.widget.components.SectionHeader
|
|
||||||
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.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.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.HeatmapCalendarTopBar
|
||||||
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
|
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
|
||||||
@@ -96,9 +93,11 @@ import io.legado.app.ui.widget.components.heatmap.WeekdayLabelsColumn
|
|||||||
import io.legado.app.ui.widget.components.heatmap.rememberDateRange
|
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.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.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
|
||||||
|
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
@@ -164,7 +163,7 @@ fun ReadRecordScreen(
|
|||||||
TopbarNavigationButton(onClick = onBackClick)
|
TopbarNavigationButton(onClick = onBackClick)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = {
|
AppIconButton(onClick = {
|
||||||
val newMode = when (displayMode) {
|
val newMode = when (displayMode) {
|
||||||
DisplayMode.AGGREGATE -> DisplayMode.TIMELINE
|
DisplayMode.AGGREGATE -> DisplayMode.TIMELINE
|
||||||
DisplayMode.TIMELINE -> DisplayMode.LATEST
|
DisplayMode.TIMELINE -> DisplayMode.LATEST
|
||||||
@@ -178,13 +177,16 @@ fun ReadRecordScreen(
|
|||||||
DisplayMode.LATEST -> Icons.AutoMirrored.Filled.List
|
DisplayMode.LATEST -> Icons.AutoMirrored.Filled.List
|
||||||
}
|
}
|
||||||
val description = if (displayMode == DisplayMode.AGGREGATE) "Switch to Timeline" else "Switch to Aggregate"
|
val description = if (displayMode == DisplayMode.AGGREGATE) "Switch to Timeline" else "Switch to Aggregate"
|
||||||
Icon(icon, description)
|
AppIcon(icon, description)
|
||||||
}
|
}
|
||||||
IconButton(onClick = { showCalendar = !showCalendar }) {
|
AppIconButton(onClick = { showCalendar = !showCalendar }) {
|
||||||
Icon(Icons.Default.CalendarMonth, contentDescription = "Toggle Calendar")
|
AppIcon(
|
||||||
|
Icons.Default.CalendarMonth,
|
||||||
|
contentDescription = "Toggle Calendar"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = { showSearch = !showSearch }) {
|
AppIconButton(onClick = { showSearch = !showSearch }) {
|
||||||
Icon(Icons.Default.Search, contentDescription = null)
|
AppIcon(Icons.Default.Search, contentDescription = null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
@@ -314,24 +316,23 @@ fun ReadRecordScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showCalendar) {
|
AppModalBottomSheet(
|
||||||
GlassModalBottomSheet(
|
show = showCalendar,
|
||||||
onDismissRequest = { showCalendar = false }
|
onDismissRequest = { showCalendar = false }
|
||||||
) {
|
) {
|
||||||
HeatmapCalendarSection(
|
HeatmapCalendarSection(
|
||||||
dailyReadCounts = state.dailyReadCounts,
|
dailyReadCounts = state.dailyReadCounts,
|
||||||
dailyReadTimes = state.dailyReadTimes,
|
dailyReadTimes = state.dailyReadTimes,
|
||||||
selectedDate = state.selectedDate,
|
selectedDate = state.selectedDate,
|
||||||
onDateSelected = { date ->
|
onDateSelected = { date ->
|
||||||
viewModel.setSelectedDate(date)
|
viewModel.setSelectedDate(date)
|
||||||
showCalendar = false
|
showCalendar = false
|
||||||
},
|
},
|
||||||
onClearDate = {
|
onClearDate = {
|
||||||
viewModel.setSelectedDate(null)
|
viewModel.setSelectedDate(null)
|
||||||
showCalendar = false
|
showCalendar = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -869,28 +870,27 @@ fun DateHeader(
|
|||||||
date: String,
|
date: String,
|
||||||
dailyTotalTime: Long? = null
|
dailyTotalTime: Long? = null
|
||||||
) {
|
) {
|
||||||
val dateText = formatFriendlyDate(date)
|
CollapsibleHeader(
|
||||||
SectionHeader(
|
showIcon = false,
|
||||||
|
isCollapsed = false,
|
||||||
|
onToggle = { },
|
||||||
|
title = "",
|
||||||
titleContent = {
|
titleContent = {
|
||||||
Column {
|
val dateText = formatFriendlyDate(date)
|
||||||
|
AppText(
|
||||||
|
text = dateText,
|
||||||
|
style = LegadoTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = LegadoTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
dailyTotalTime?.let { total ->
|
||||||
AppText(
|
AppText(
|
||||||
text = dateText,
|
text = "已读 ${formatDuring(total)}",
|
||||||
style = LegadoTheme.typography.bodyMedium,
|
style = LegadoTheme.typography.bodySmall,
|
||||||
fontWeight = FontWeight.Bold,
|
color = LegadoTheme.colorScheme.onSurface
|
||||||
color = MaterialTheme.colorScheme.secondary
|
|
||||||
)
|
)
|
||||||
|
|
||||||
dailyTotalTime?.let { total ->
|
|
||||||
AppText(
|
|
||||||
text = "已读 ${formatDuring(total)}",
|
|
||||||
style = LegadoTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
detailContent = null,
|
|
||||||
horizontalArrangement = Arrangement.Start
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -912,14 +912,12 @@ fun ReadingSummaryCard(
|
|||||||
|
|
||||||
val totalDurationMinutes = totalTimeMillis / 60000
|
val totalDurationMinutes = totalTimeMillis / 60000
|
||||||
|
|
||||||
Card(
|
GlassCard(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
colors = CardDefaults.cardColors(
|
containerColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -934,7 +932,7 @@ fun ReadingSummaryCard(
|
|||||||
AppText(
|
AppText(
|
||||||
text = title,
|
text = title,
|
||||||
style = LegadoTheme.typography.labelLarge,
|
style = LegadoTheme.typography.labelLarge,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = LegadoTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
@@ -947,7 +945,7 @@ fun ReadingSummaryCard(
|
|||||||
AppText(
|
AppText(
|
||||||
text = "$bookCount",
|
text = "$bookCount",
|
||||||
style = LegadoTheme.typography.headlineMedium,
|
style = LegadoTheme.typography.headlineMedium,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = LegadoTheme.colorScheme.primary,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
)
|
)
|
||||||
AppText(
|
AppText(
|
||||||
@@ -965,7 +963,7 @@ fun ReadingSummaryCard(
|
|||||||
AppText(
|
AppText(
|
||||||
text = "共阅读 $timeString",
|
text = "共阅读 $timeString",
|
||||||
style = LegadoTheme.typography.bodySmall,
|
style = LegadoTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ fun SearchHistoryList(
|
|||||||
AppText(
|
AppText(
|
||||||
text = "搜索历史",
|
text = "搜索历史",
|
||||||
style = LegadoTheme.typography.titleSmallEmphasized,
|
style = LegadoTheme.typography.titleSmallEmphasized,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = LegadoTheme.colorScheme.primary,
|
||||||
modifier = Modifier.align(Alignment.Center)
|
modifier = Modifier.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
SmallAnimatedActionButton(
|
SmallAnimatedActionButton(
|
||||||
|
|||||||
@@ -313,33 +313,33 @@ fun TocScreen(
|
|||||||
when (pagerState.currentPage) {
|
when (pagerState.currentPage) {
|
||||||
0 -> {
|
0 -> {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("使用替换规则") },
|
text = "使用替换规则",
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
Checkbox(checked = useReplace, onCheckedChange = null)
|
Checkbox(checked = useReplace, onCheckedChange = null)
|
||||||
},
|
},
|
||||||
onClick = { viewModel.toggleUseReplace() }
|
onClick = { viewModel.toggleUseReplace() }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("显示字数") },
|
text = "显示字数",
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
Checkbox(checked = showWordCount, onCheckedChange = null)
|
Checkbox(checked = showWordCount, onCheckedChange = null)
|
||||||
},
|
},
|
||||||
onClick = { viewModel.toggleShowWordCount() }
|
onClick = { viewModel.toggleShowWordCount() }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("反转目录") },
|
text = "反转目录",
|
||||||
onClick = { viewModel.reverseToc() }
|
onClick = { viewModel.reverseToc() }
|
||||||
)
|
)
|
||||||
PillDivider()
|
PillDivider()
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("替换规则") },
|
text = "替换规则",
|
||||||
onClick = {
|
onClick = {
|
||||||
onOpenReplaceRule(null)
|
onOpenReplaceRule(null)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("新建替换规则") },
|
text = "新建替换规则",
|
||||||
onClick = {
|
onClick = {
|
||||||
val scopes = mutableListOf<String>()
|
val scopes = mutableListOf<String>()
|
||||||
book?.name?.let { scopes.add(it) }
|
book?.name?.let { scopes.add(it) }
|
||||||
@@ -359,7 +359,7 @@ fun TocScreen(
|
|||||||
if (book?.isLocal == true) {
|
if (book?.isLocal == true) {
|
||||||
PillHeaderDivider(title = "本地书籍选项")
|
PillHeaderDivider(title = "本地书籍选项")
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("本地书籍目录规则") },
|
text = "本地书籍目录规则",
|
||||||
onClick = {
|
onClick = {
|
||||||
val intent =
|
val intent =
|
||||||
Intent(context, TxtTocRuleActivity::class.java).apply {
|
Intent(context, TxtTocRuleActivity::class.java).apply {
|
||||||
@@ -370,7 +370,7 @@ fun TocScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("拆分超长章节") },
|
text = "拆分超长章节",
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = viewModel.isSplitLongChapter,
|
checked = viewModel.isSplitLongChapter,
|
||||||
@@ -387,7 +387,7 @@ fun TocScreen(
|
|||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("导出书签为JSON") },
|
text = "导出书签为JSON",
|
||||||
onClick = {
|
onClick = {
|
||||||
val dateFormat = SimpleDateFormat(
|
val dateFormat = SimpleDateFormat(
|
||||||
"yyyyMMdd_HHmm",
|
"yyyyMMdd_HHmm",
|
||||||
@@ -399,7 +399,7 @@ fun TocScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("导出书签为MarkDown") },
|
text = "导出书签为MarkDown",
|
||||||
onClick = {
|
onClick = {
|
||||||
val dateFormat = SimpleDateFormat(
|
val dateFormat = SimpleDateFormat(
|
||||||
"yyyyMMdd_HHmm",
|
"yyyyMMdd_HHmm",
|
||||||
@@ -465,13 +465,13 @@ fun TocScreen(
|
|||||||
onDismissRequest = { showVolumeMenu = false }
|
onDismissRequest = { showVolumeMenu = false }
|
||||||
) {
|
) {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("展开所有卷") },
|
text = "展开所有卷",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.expandAllVolumes(); showVolumeMenu = false
|
viewModel.expandAllVolumes(); showVolumeMenu = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("收起所有卷") },
|
text = "收起所有卷",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.collapseAllVolumes(); showVolumeMenu = false
|
viewModel.collapseAllVolumes(); showVolumeMenu = false
|
||||||
}
|
}
|
||||||
@@ -483,7 +483,7 @@ fun TocScreen(
|
|||||||
PillHeaderDivider(title = "快速跳转")
|
PillHeaderDivider(title = "快速跳转")
|
||||||
volumeItems.forEach { uiItem ->
|
volumeItems.forEach { uiItem ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(uiItem.title) },
|
text = uiItem.title,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val targetIndex =
|
val targetIndex =
|
||||||
@@ -594,20 +594,19 @@ fun TocScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editingBookmark != null) {
|
BookmarkEditSheet(
|
||||||
BookmarkEditSheet(
|
show = editingBookmark != null,
|
||||||
bookmark = editingBookmark!!,
|
bookmark = editingBookmark!!,
|
||||||
onDismiss = { editingBookmark = null },
|
onDismiss = { editingBookmark = null },
|
||||||
onSave = { updatedBookmark ->
|
onSave = { updatedBookmark ->
|
||||||
viewModel.updateBookmark(updatedBookmark)
|
viewModel.updateBookmark(updatedBookmark)
|
||||||
editingBookmark = null
|
editingBookmark = null
|
||||||
},
|
},
|
||||||
onDelete = { bookmarkToDelete ->
|
onDelete = { bookmarkToDelete ->
|
||||||
viewModel.deleteBookmark(bookmarkToDelete)
|
viewModel.deleteBookmark(bookmarkToDelete)
|
||||||
editingBookmark = null
|
editingBookmark = null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -44,13 +43,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.data.entities.TxtTocRule
|
import io.legado.app.data.entities.TxtTocRule
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
|
||||||
import io.legado.app.ui.widget.components.ActionItem
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
|
|
||||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
||||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||||
@@ -154,54 +151,49 @@ fun TxtRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showExportSheet) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showExportSheet,
|
||||||
onDismissRequest = { showExportSheet = false },
|
onDismissRequest = { showExportSheet = false },
|
||||||
title = stringResource(R.string.export),
|
title = stringResource(R.string.export),
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
exportDoc.launch("exportDictRule.json")
|
exportDoc.launch("exportDictRule.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
allowExtensions = arrayOf("json")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showImportSheet) {
|
|
||||||
FilePickerSheet(
|
|
||||||
onDismissRequest = { showImportSheet = false },
|
|
||||||
title = stringResource(R.string.import_txt_toc_rule),
|
|
||||||
onSelectSysFile = { types ->
|
|
||||||
importDoc.launch(types)
|
|
||||||
showImportSheet = false
|
|
||||||
},
|
|
||||||
onManualInput = {
|
|
||||||
showUrlInput = true
|
|
||||||
showImportSheet = false
|
|
||||||
},
|
|
||||||
allowExtensions = arrayOf("json", "txt")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
(importState as? BaseImportUiState.Success<TxtTocRule>)?.let { state ->
|
FilePickerSheet(
|
||||||
BatchImportDialog(
|
show = showImportSheet,
|
||||||
title = "导入词典规则",
|
onDismissRequest = { showImportSheet = false },
|
||||||
importState = state,
|
title = stringResource(R.string.import_txt_toc_rule),
|
||||||
onDismissRequest = { viewModel.cancelImport() },
|
onSelectSysFile = { types ->
|
||||||
onToggleItem = { viewModel.toggleImportSelection(it) },
|
importDoc.launch(types)
|
||||||
onToggleAll = { viewModel.toggleImportAll(it) },
|
showImportSheet = false
|
||||||
onConfirm = { viewModel.saveImportedRules() },
|
},
|
||||||
itemContent = { rule, _ ->
|
onManualInput = {
|
||||||
Column {
|
showUrlInput = true
|
||||||
AppText(rule.name, style = LegadoTheme.typography.titleMedium)
|
showImportSheet = false
|
||||||
AppText(rule.rule, style = LegadoTheme.typography.bodySmall, maxLines = 1)
|
},
|
||||||
}
|
allowExtensions = arrayOf("json", "txt")
|
||||||
}
|
)
|
||||||
)
|
|
||||||
}
|
BatchImportDialog(
|
||||||
|
title = "导入词典规则",
|
||||||
|
importState = importState,
|
||||||
|
onDismissRequest = { viewModel.cancelImport() },
|
||||||
|
onToggleItem = { viewModel.toggleImportSelection(it) },
|
||||||
|
onToggleAll = { viewModel.toggleImportAll(it) },
|
||||||
|
onConfirm = { viewModel.saveImportedRules() },
|
||||||
|
itemTitle = { rule -> rule.name },
|
||||||
|
itemSubtitle = { rule ->
|
||||||
|
rule.rule.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
||||||
if (!reorderableState.isAnyItemDragging) {
|
if (!reorderableState.isAnyItemDragging) {
|
||||||
@@ -229,48 +221,47 @@ fun TxtRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showEditSheet) {
|
RuleEditSheet(
|
||||||
RuleEditSheet(
|
show = showEditSheet,
|
||||||
rule = editingRule,
|
rule = editingRule,
|
||||||
title = stringResource(R.string.txt_toc_rule),
|
title = stringResource(R.string.txt_toc_rule),
|
||||||
label1 = stringResource(R.string.regex),
|
label1 = stringResource(R.string.regex),
|
||||||
label2 = stringResource(R.string.example),
|
label2 = stringResource(R.string.example),
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
showEditSheet = false
|
showEditSheet = false
|
||||||
editingRule = null
|
editingRule = null
|
||||||
},
|
},
|
||||||
onSave = { updatedRule ->
|
onSave = { updatedRule ->
|
||||||
//TODO:我很想把他改为自增主键,但为了兼容性日后再说
|
//TODO:我很想把他改为自增主键,但为了兼容性日后再说
|
||||||
if (editingRule == null) {
|
if (editingRule == null) {
|
||||||
viewModel.insert(updatedRule)
|
viewModel.insert(updatedRule)
|
||||||
} else {
|
} else {
|
||||||
viewModel.update(updatedRule)
|
viewModel.update(updatedRule)
|
||||||
}
|
|
||||||
showEditSheet = false
|
|
||||||
editingRule = null
|
|
||||||
},
|
|
||||||
onCopy = { viewModel.copyRule(it) },
|
|
||||||
onPaste = { viewModel.pasteRule() },
|
|
||||||
toFields = { r ->
|
|
||||||
RuleEditFields(
|
|
||||||
name = r?.name ?: "",
|
|
||||||
rule1 = r?.rule ?: "",
|
|
||||||
rule2 = r?.example ?: ""
|
|
||||||
)
|
|
||||||
},
|
|
||||||
fromFields = { fields, old ->
|
|
||||||
old?.copy(
|
|
||||||
name = fields.name,
|
|
||||||
rule = fields.rule1,
|
|
||||||
example = fields.rule2
|
|
||||||
) ?: TxtTocRule(
|
|
||||||
name = fields.name,
|
|
||||||
rule = fields.rule1,
|
|
||||||
example = fields.rule2
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
showEditSheet = false
|
||||||
}
|
editingRule = null
|
||||||
|
},
|
||||||
|
onCopy = { viewModel.copyRule(it) },
|
||||||
|
onPaste = { viewModel.pasteRule() },
|
||||||
|
toFields = { r ->
|
||||||
|
RuleEditFields(
|
||||||
|
name = r?.name ?: "",
|
||||||
|
rule1 = r?.rule ?: "",
|
||||||
|
rule2 = r?.example ?: ""
|
||||||
|
)
|
||||||
|
},
|
||||||
|
fromFields = { fields, old ->
|
||||||
|
old?.copy(
|
||||||
|
name = fields.name,
|
||||||
|
rule = fields.rule1,
|
||||||
|
example = fields.rule2
|
||||||
|
) ?: TxtTocRule(
|
||||||
|
name = fields.name,
|
||||||
|
rule = fields.rule1,
|
||||||
|
example = fields.rule2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
RuleListScaffold(
|
RuleListScaffold(
|
||||||
title = if (isPickMode) "选择目录规则" else "目录规则",
|
title = if (isPickMode) "选择目录规则" else "目录规则",
|
||||||
@@ -312,7 +303,7 @@ fun TxtRuleScreen(
|
|||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_str)) },
|
text = stringResource(R.string.import_str),
|
||||||
onClick = { showImportSheet = true; dismiss() },
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -453,31 +453,30 @@ fun BackupConfigScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBackupFilePicker) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showBackupFilePicker,
|
||||||
onDismissRequest = { showBackupFilePicker = false },
|
onDismissRequest = { showBackupFilePicker = false },
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showBackupFilePicker = false
|
showBackupFilePicker = false
|
||||||
try {
|
try {
|
||||||
selectBackupPathLauncher.launch(null)
|
selectBackupPathLauncher.launch(null)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
if (showRestoreFilePicker) {
|
|
||||||
FilePickerSheet(
|
FilePickerSheet(
|
||||||
onDismissRequest = { showRestoreFilePicker = false },
|
show = showRestoreFilePicker,
|
||||||
onSelectSysDir = {
|
onDismissRequest = { showRestoreFilePicker = false },
|
||||||
showRestoreFilePicker = false
|
onSelectSysDir = {
|
||||||
try {
|
showRestoreFilePicker = false
|
||||||
backupAndSelectLauncher.launch(null)
|
try {
|
||||||
} catch (e: Exception) {
|
backupAndSelectLauncher.launch(null)
|
||||||
}
|
} catch (e: Exception) {
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
if (showRestoreSheet && backupNames.isNotEmpty()) {
|
if (showRestoreSheet && backupNames.isNotEmpty()) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
|||||||
@@ -246,17 +246,17 @@ fun CoverConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showCoverRuleSheet) {
|
CoverRuleConfigSheet(
|
||||||
CoverRuleConfigSheet(onDismissRequest = { showCoverRuleSheet = false })
|
show = showCoverRuleSheet,
|
||||||
}
|
onDismissRequest = { showCoverRuleSheet = false }
|
||||||
|
)
|
||||||
|
|
||||||
manageKey?.let { key ->
|
CoverManageSheet(
|
||||||
CoverManageSheet(
|
show = manageKey != null,
|
||||||
preferenceKey = key,
|
preferenceKey = manageKey!!,
|
||||||
onDismissRequest = { manageKey = null },
|
onDismissRequest = { manageKey = null },
|
||||||
viewModel = viewModel
|
viewModel = viewModel
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
showColorPickerByField?.let { field ->
|
showColorPickerByField?.let { field ->
|
||||||
val initialColor = when (field) {
|
val initialColor = when (field) {
|
||||||
@@ -268,6 +268,7 @@ fun CoverConfigScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ColorPickerSheet(
|
ColorPickerSheet(
|
||||||
|
show = showColorPickerByField != null,
|
||||||
initialColor = initialColor,
|
initialColor = initialColor,
|
||||||
onDismissRequest = { showColorPickerByField = null },
|
onDismissRequest = { showColorPickerByField = null },
|
||||||
onColorSelected = { color ->
|
onColorSelected = { color ->
|
||||||
|
|||||||
@@ -37,12 +37,13 @@ import io.legado.app.R
|
|||||||
import io.legado.app.constant.PreferKey
|
import io.legado.app.constant.PreferKey
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CoverManageSheet(
|
fun CoverManageSheet(
|
||||||
|
show: Boolean,
|
||||||
preferenceKey: String,
|
preferenceKey: String,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
viewModel: CoverConfigViewModel
|
viewModel: CoverConfigViewModel
|
||||||
@@ -62,7 +63,8 @@ fun CoverManageSheet(
|
|||||||
}
|
}
|
||||||
val coverList = currentCovers.split(",").filter { it.isNotBlank() }
|
val coverList = currentCovers.split(",").filter { it.isNotBlank() }
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -142,14 +144,13 @@ fun CoverManageSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFilePicker) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showFilePicker,
|
||||||
onDismissRequest = { showFilePicker = false },
|
onDismissRequest = { showFilePicker = false },
|
||||||
onSelectSysFiles = {
|
onSelectSysFiles = {
|
||||||
selectImages.launch("image/*")
|
selectImages.launch("image/*")
|
||||||
showFilePicker = false
|
showFilePicker = false
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
|
allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.model.BookCover
|
import io.legado.app.model.BookCover
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.settingItem.InputSettingItem
|
import io.legado.app.ui.widget.components.settingItem.InputSettingItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -36,6 +36,7 @@ import splitties.init.appCtx
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CoverRuleConfigSheet(
|
fun CoverRuleConfigSheet(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
var enable by remember { mutableStateOf(false) }
|
var enable by remember { mutableStateOf(false) }
|
||||||
@@ -51,7 +52,8 @@ fun CoverRuleConfigSheet(
|
|||||||
coverRule = rule.coverRule
|
coverRule = rule.coverRule
|
||||||
}
|
}
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -21,19 +20,22 @@ import io.legado.app.R
|
|||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.checkBox.CheckboxGroupContainer
|
import io.legado.app.ui.widget.components.checkBox.CheckboxGroupContainer
|
||||||
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.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
|
import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
|
||||||
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckSourceBottomSheet(
|
fun CheckSourceBottomSheet(
|
||||||
|
show: Boolean,
|
||||||
viewModel: OtherConfigViewModel = koinViewModel(),
|
viewModel: OtherConfigViewModel = koinViewModel(),
|
||||||
onDismiss: () -> Unit
|
onDismiss: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismiss
|
onDismissRequest = onDismiss
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
+4
-2
@@ -42,7 +42,7 @@ import io.legado.app.help.DirectLinkUpload
|
|||||||
import io.legado.app.lib.dialogs.selector
|
import io.legado.app.lib.dialogs.selector
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
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.modalBottomSheet.GlassModalBottomSheet
|
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 io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
import io.legado.app.utils.fromJsonObject
|
import io.legado.app.utils.fromJsonObject
|
||||||
@@ -53,6 +53,7 @@ import io.legado.app.utils.toastOnUi
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun DirectLinkUploadBottomSheet(
|
fun DirectLinkUploadBottomSheet(
|
||||||
|
show: Boolean,
|
||||||
viewModel: OtherConfigViewModel,
|
viewModel: OtherConfigViewModel,
|
||||||
onDismiss: () -> Unit
|
onDismiss: () -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -64,7 +65,8 @@ fun DirectLinkUploadBottomSheet(
|
|||||||
viewModel.initDirectLinkRule()
|
viewModel.initDirectLinkRule()
|
||||||
}
|
}
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismiss
|
onDismissRequest = onDismiss
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -398,33 +398,30 @@ fun OtherConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFilePicker) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showFilePicker,
|
||||||
onDismissRequest = { showFilePicker = false },
|
onDismissRequest = { showFilePicker = false },
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showFilePicker = false
|
showFilePicker = false
|
||||||
try {
|
try {
|
||||||
selectDocTree.launch(null)
|
selectDocTree.launch(null)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
if (showCheckSourceSheet) {
|
CheckSourceBottomSheet(
|
||||||
CheckSourceBottomSheet(
|
show = showCheckSourceSheet,
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onDismiss = { showCheckSourceSheet = false }
|
onDismiss = { showCheckSourceSheet = false }
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showDirectLinkUploadSheet) {
|
DirectLinkUploadBottomSheet(
|
||||||
DirectLinkUploadBottomSheet(
|
show = showDirectLinkUploadSheet,
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onDismiss = { showDirectLinkUploadSheet = false }
|
onDismiss = { showDirectLinkUploadSheet = false }
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showClearCacheDialog) {
|
if (showClearCacheDialog) {
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
|
|||||||
@@ -23,18 +23,19 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
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.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PageKeySheet(
|
fun PageKeySheet(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
var prevKeys by remember { mutableStateOf(ReadConfig.prevKeys) }
|
var prevKeys by remember { mutableStateOf(ReadConfig.prevKeys) }
|
||||||
var nextKeys by remember { mutableStateOf(ReadConfig.nextKeys) }
|
var nextKeys by remember { mutableStateOf(ReadConfig.nextKeys) }
|
||||||
|
|
||||||
GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
|
AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.Companion
|
modifier = Modifier.Companion
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
@@ -320,7 +320,8 @@ fun ReadConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showPageKeySheet) {
|
PageKeySheet(
|
||||||
PageKeySheet(onDismissRequest = { showPageKeySheet = false })
|
show = showPageKeySheet,
|
||||||
}
|
onDismissRequest = { showPageKeySheet = false }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
+13
-12
@@ -32,7 +32,7 @@ import coil.compose.AsyncImage
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
@@ -40,6 +40,7 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BackgroundImageManageSheet(
|
fun BackgroundImageManageSheet(
|
||||||
|
show: Boolean,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
viewModel: ThemeConfigViewModel = koinViewModel()
|
viewModel: ThemeConfigViewModel = koinViewModel()
|
||||||
@@ -65,7 +66,8 @@ fun BackgroundImageManageSheet(
|
|||||||
ThemeConfig.bgImageLight
|
ThemeConfig.bgImageLight
|
||||||
}
|
}
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -140,14 +142,13 @@ fun BackgroundImageManageSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showFilePicker) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showFilePicker,
|
||||||
onDismissRequest = { showFilePicker = false },
|
onDismissRequest = { showFilePicker = false },
|
||||||
onSelectSysFile = {
|
onSelectSysFile = {
|
||||||
selectImage.launch("image/*")
|
selectImage.launch("image/*")
|
||||||
showFilePicker = false
|
showFilePicker = false
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
|
allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ import io.legado.app.ui.main.Launcher5
|
|||||||
import io.legado.app.ui.main.Launcher6
|
import io.legado.app.ui.main.Launcher6
|
||||||
import io.legado.app.ui.main.LauncherW
|
import io.legado.app.ui.main.LauncherW
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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 io.legado.app.utils.getCompatDrawable
|
import io.legado.app.utils.getCompatDrawable
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
@@ -47,6 +47,7 @@ import splitties.init.appCtx
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LauncherIconPickerSheet(
|
fun LauncherIconPickerSheet(
|
||||||
|
show: Boolean,
|
||||||
selectedValue: String,
|
selectedValue: String,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onValueChange: (String) -> Unit
|
onValueChange: (String) -> Unit
|
||||||
@@ -54,7 +55,8 @@ fun LauncherIconPickerSheet(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val icons = LauncherIcons.list
|
val icons = LauncherIcons.list
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ object ThemeConfig {
|
|||||||
|
|
||||||
var enableBlur by prefDelegate(PreferKey.enableBlur, false)
|
var enableBlur by prefDelegate(PreferKey.enableBlur, false)
|
||||||
|
|
||||||
var enableProgressiveBlur by prefDelegate(PreferKey.enableProgressiveBlur, true)
|
var enableProgressiveBlur by prefDelegate(PreferKey.enableProgressiveBlur, false)
|
||||||
|
|
||||||
var useFlexibleTopAppBar by prefDelegate(PreferKey.useFlexibleTopAppBar, true)
|
var useFlexibleTopAppBar by prefDelegate(PreferKey.useFlexibleTopAppBar, true)
|
||||||
|
|
||||||
var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot")
|
var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot")
|
||||||
|
|
||||||
//m3 or miuix
|
//m3 or miuix
|
||||||
var composeEngine by prefDelegate(PreferKey.composeEngine, "m3")
|
var composeEngine by prefDelegate(PreferKey.composeEngine, "material")
|
||||||
|
|
||||||
var materialVersion by prefDelegate(PreferKey.materialVersion, "material3")
|
var materialVersion by prefDelegate(PreferKey.materialVersion, "material3")
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -44,6 +45,7 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
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.ToggleButton
|
import androidx.compose.material3.ToggleButton
|
||||||
import androidx.compose.material3.ToggleButtonDefaults
|
import androidx.compose.material3.ToggleButtonDefaults
|
||||||
@@ -95,6 +97,10 @@ import io.legado.app.utils.postEvent
|
|||||||
import io.legado.app.utils.restart
|
import io.legado.app.utils.restart
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
import top.yukonga.miuix.kmp.basic.Card as MiuixCard
|
||||||
|
import top.yukonga.miuix.kmp.basic.CardDefaults as MiuixCardDefaults
|
||||||
|
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -137,9 +143,10 @@ fun ThemeConfigScreen(
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
val composeEngine = remember { ThemeConfig.composeEngine }
|
val composeEngine = ThemeConfig.composeEngine
|
||||||
val isMiuixEngine =
|
val isMiuixEngine = remember(composeEngine) {
|
||||||
remember(composeEngine) { ThemeResolver.isMiuixEngine(composeEngine) }
|
ThemeResolver.isMiuixEngine(composeEngine)
|
||||||
|
}
|
||||||
val isDarkTheme = when (selectedThemeMode) {
|
val isDarkTheme = when (selectedThemeMode) {
|
||||||
"1" -> false
|
"1" -> false
|
||||||
"2" -> true
|
"2" -> true
|
||||||
@@ -169,6 +176,20 @@ fun ThemeConfigScreen(
|
|||||||
themeItems.zip(themeValues).toList()
|
themeItems.zip(themeValues).toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isMiuixEngine) {
|
||||||
|
MiuixCard(
|
||||||
|
cornerRadius = 16.dp,
|
||||||
|
insideMargin = PaddingValues(16.dp),
|
||||||
|
colors = MiuixCardDefaults.defaultColors(
|
||||||
|
color = MiuixTheme.colorScheme.primaryVariant,
|
||||||
|
contentColor = MiuixTheme.colorScheme.onPrimary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
MiuixText("Miuix 目前为测试主题,且不对基于View的界面生效!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SplicedColumnGroup(title = stringResource(R.string.theme)) {
|
SplicedColumnGroup(title = stringResource(R.string.theme)) {
|
||||||
if (isMiuixEngine) {
|
if (isMiuixEngine) {
|
||||||
DropdownListSettingItem(
|
DropdownListSettingItem(
|
||||||
@@ -253,7 +274,9 @@ fun ThemeConfigScreen(
|
|||||||
selectedValue = ThemeConfig.composeEngine,
|
selectedValue = ThemeConfig.composeEngine,
|
||||||
displayEntries = stringArrayResource(R.array.composeEngine),
|
displayEntries = stringArrayResource(R.array.composeEngine),
|
||||||
entryValues = stringArrayResource(R.array.composeEngine_value),
|
entryValues = stringArrayResource(R.array.composeEngine_value),
|
||||||
onValueChange = { ThemeConfig.composeEngine = it }
|
onValueChange = {
|
||||||
|
ThemeConfig.composeEngine = it
|
||||||
|
}
|
||||||
)
|
)
|
||||||
SliderSettingItem(
|
SliderSettingItem(
|
||||||
title = stringResource(R.string.font_scale),
|
title = stringResource(R.string.font_scale),
|
||||||
@@ -531,47 +554,46 @@ fun ThemeConfigScreen(
|
|||||||
|
|
||||||
manageKey?.let { isDark ->
|
manageKey?.let { isDark ->
|
||||||
BackgroundImageManageSheet(
|
BackgroundImageManageSheet(
|
||||||
|
show = true,
|
||||||
isDarkTheme = isDark,
|
isDarkTheme = isDark,
|
||||||
onDismissRequest = { manageKey = null }
|
onDismissRequest = { manageKey = null }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showColorPicker) {
|
|
||||||
ColorPickerSheet(
|
|
||||||
initialColor = primaryColorValue.value,
|
|
||||||
onDismissRequest = { showColorPicker = false },
|
|
||||||
onColorSelected = { color ->
|
|
||||||
primaryColorValue.value = color
|
|
||||||
ThemeConfig.cPrimary = color
|
|
||||||
ThemeStore.editTheme(context)
|
|
||||||
.primaryColor(color)
|
|
||||||
.apply()
|
|
||||||
DynamicColors.applyToActivitiesIfAvailable(
|
|
||||||
context.applicationContext as android.app.Application,
|
|
||||||
DynamicColorsOptions.Builder()
|
|
||||||
.setContentBasedSource(context.primaryColor)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showLauncherIconPicker) {
|
ColorPickerSheet(
|
||||||
LauncherIconPickerSheet(
|
show = showColorPicker,
|
||||||
selectedValue = ThemeConfig.launcherIcon,
|
initialColor = primaryColorValue.value,
|
||||||
onDismissRequest = { showLauncherIconPicker = false },
|
onDismissRequest = { showColorPicker = false },
|
||||||
onValueChange = {
|
onColorSelected = { color ->
|
||||||
ThemeConfig.launcherIcon = it
|
primaryColorValue.value = color
|
||||||
LauncherIconHelp.changeIcon(it)
|
ThemeConfig.cPrimary = color
|
||||||
}
|
ThemeStore.editTheme(context)
|
||||||
)
|
.primaryColor(color)
|
||||||
}
|
.apply()
|
||||||
|
DynamicColors.applyToActivitiesIfAvailable(
|
||||||
|
context.applicationContext as android.app.Application,
|
||||||
|
DynamicColorsOptions.Builder()
|
||||||
|
.setContentBasedSource(context.primaryColor)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (showThemeListDialog) {
|
LauncherIconPickerSheet(
|
||||||
ThemeListDialog(
|
show = showLauncherIconPicker,
|
||||||
onDismissRequest = { showThemeListDialog = false }
|
selectedValue = ThemeConfig.launcherIcon,
|
||||||
)
|
onDismissRequest = { showLauncherIconPicker = false },
|
||||||
}
|
onValueChange = {
|
||||||
|
ThemeConfig.launcherIcon = it
|
||||||
|
LauncherIconHelp.changeIcon(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ThemeListDialog(
|
||||||
|
show = showThemeListDialog,
|
||||||
|
onDismissRequest = { showThemeListDialog = false }
|
||||||
|
)
|
||||||
|
|
||||||
saveThemeKey?.let { key ->
|
saveThemeKey?.let { key ->
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -652,7 +674,7 @@ fun ThemeModeSelector(
|
|||||||
|
|
||||||
Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
|
Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
|
||||||
|
|
||||||
AppText(text = label)
|
Text(text = label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import io.legado.app.help.config.OldThemeConfig
|
|||||||
import io.legado.app.lib.theme.primaryColor
|
import io.legado.app.lib.theme.primaryColor
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
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.modalBottomSheet.GlassModalBottomSheet
|
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 io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
import io.legado.app.utils.getClipText
|
import io.legado.app.utils.getClipText
|
||||||
@@ -48,6 +48,7 @@ import io.legado.app.utils.toastOnUi
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ThemeListDialog(
|
fun ThemeListDialog(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -55,7 +56,8 @@ fun ThemeListDialog(
|
|||||||
var deleteIndex by remember { mutableStateOf<Int?>(null) }
|
var deleteIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
val themeList = remember(listVersion) { OldThemeConfig.configList.toList() }
|
val themeList = remember(listVersion) { OldThemeConfig.configList.toList() }
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ 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.data.entities.DictRule
|
import io.legado.app.data.entities.DictRule
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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
|
||||||
|
|
||||||
@@ -56,7 +56,8 @@ fun DictRuleEditSheet(
|
|||||||
var showRule by remember(rule) { mutableStateOf(rule?.showRule ?: "") }
|
var showRule by remember(rule) { mutableStateOf(rule?.showRule ?: "") }
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = true,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -44,13 +43,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.data.entities.DictRule
|
import io.legado.app.data.entities.DictRule
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
|
||||||
import io.legado.app.ui.widget.components.ActionItem
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
|
|
||||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
||||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||||
@@ -153,54 +150,49 @@ fun DictRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showExportSheet) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showExportSheet,
|
||||||
onDismissRequest = { showExportSheet = false },
|
onDismissRequest = { showExportSheet = false },
|
||||||
title = stringResource(R.string.export),
|
title = stringResource(R.string.export),
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
exportDoc.launch("exportDictRule.json")
|
exportDoc.launch("exportDictRule.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
allowExtensions = arrayOf("json")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showImportSheet) {
|
|
||||||
FilePickerSheet(
|
|
||||||
onDismissRequest = { showImportSheet = false },
|
|
||||||
title = stringResource(R.string.import_dict_rule),
|
|
||||||
onSelectSysFile = { types ->
|
|
||||||
importDoc.launch(types)
|
|
||||||
showImportSheet = false
|
|
||||||
},
|
|
||||||
onManualInput = {
|
|
||||||
showUrlInput = true
|
|
||||||
showImportSheet = false
|
|
||||||
},
|
|
||||||
allowExtensions = arrayOf("json", "txt")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
(importState as? BaseImportUiState.Success<DictRule>)?.let { state ->
|
FilePickerSheet(
|
||||||
BatchImportDialog(
|
show = showImportSheet,
|
||||||
title = "导入词典规则",
|
onDismissRequest = { showImportSheet = false },
|
||||||
importState = state,
|
title = stringResource(R.string.import_dict_rule),
|
||||||
onDismissRequest = { viewModel.cancelImport() },
|
onSelectSysFile = { types ->
|
||||||
onToggleItem = { viewModel.toggleImportSelection(it) },
|
importDoc.launch(types)
|
||||||
onToggleAll = { viewModel.toggleImportAll(it) },
|
showImportSheet = false
|
||||||
onConfirm = { viewModel.saveImportedRules() },
|
},
|
||||||
itemContent = { rule, _ ->
|
onManualInput = {
|
||||||
Column {
|
showUrlInput = true
|
||||||
AppText(rule.name, style = LegadoTheme.typography.titleMedium)
|
showImportSheet = false
|
||||||
AppText(rule.urlRule, style = LegadoTheme.typography.bodySmall, maxLines = 1)
|
},
|
||||||
}
|
allowExtensions = arrayOf("json", "txt")
|
||||||
}
|
)
|
||||||
)
|
|
||||||
}
|
BatchImportDialog(
|
||||||
|
title = "导入词典规则",
|
||||||
|
importState = importState,
|
||||||
|
onDismissRequest = { viewModel.cancelImport() },
|
||||||
|
onToggleItem = { viewModel.toggleImportSelection(it) },
|
||||||
|
onToggleAll = { viewModel.toggleImportAll(it) },
|
||||||
|
onConfirm = { viewModel.saveImportedRules() },
|
||||||
|
itemTitle = { rule -> rule.name },
|
||||||
|
itemSubtitle = { rule ->
|
||||||
|
rule.urlRule.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
||||||
if (!reorderableState.isAnyItemDragging) {
|
if (!reorderableState.isAnyItemDragging) {
|
||||||
@@ -228,47 +220,46 @@ fun DictRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showEditSheet) {
|
RuleEditSheet(
|
||||||
RuleEditSheet(
|
show = showEditSheet,
|
||||||
rule = editingRule,
|
rule = editingRule,
|
||||||
title = stringResource(R.string.dict_rule),
|
title = stringResource(R.string.dict_rule),
|
||||||
label1 = stringResource(R.string.url_rule),
|
label1 = stringResource(R.string.url_rule),
|
||||||
label2 = stringResource(R.string.show_rule),
|
label2 = stringResource(R.string.show_rule),
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
showEditSheet = false
|
showEditSheet = false
|
||||||
editingRule = null
|
editingRule = null
|
||||||
},
|
},
|
||||||
onSave = { updatedRule ->
|
onSave = { updatedRule ->
|
||||||
if (editingRule == null) {
|
if (editingRule == null) {
|
||||||
viewModel.insert(updatedRule)
|
viewModel.insert(updatedRule)
|
||||||
} else {
|
} else {
|
||||||
viewModel.update(updatedRule)
|
viewModel.update(updatedRule)
|
||||||
}
|
|
||||||
showEditSheet = false
|
|
||||||
editingRule = null
|
|
||||||
},
|
|
||||||
onCopy = { viewModel.copyRule(it) },
|
|
||||||
onPaste = { viewModel.pasteRule() },
|
|
||||||
toFields = { r ->
|
|
||||||
RuleEditFields(
|
|
||||||
name = r?.name ?: "",
|
|
||||||
rule1 = r?.urlRule ?: "",
|
|
||||||
rule2 = r?.showRule ?: ""
|
|
||||||
)
|
|
||||||
},
|
|
||||||
fromFields = { fields, old ->
|
|
||||||
old?.copy(
|
|
||||||
name = fields.name,
|
|
||||||
urlRule = fields.rule1,
|
|
||||||
showRule = fields.rule2
|
|
||||||
) ?: DictRule(
|
|
||||||
name = fields.name,
|
|
||||||
urlRule = fields.rule1,
|
|
||||||
showRule = fields.rule2
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
showEditSheet = false
|
||||||
}
|
editingRule = null
|
||||||
|
},
|
||||||
|
onCopy = { viewModel.copyRule(it) },
|
||||||
|
onPaste = { viewModel.pasteRule() },
|
||||||
|
toFields = { r ->
|
||||||
|
RuleEditFields(
|
||||||
|
name = r?.name ?: "",
|
||||||
|
rule1 = r?.urlRule ?: "",
|
||||||
|
rule2 = r?.showRule ?: ""
|
||||||
|
)
|
||||||
|
},
|
||||||
|
fromFields = { fields, old ->
|
||||||
|
old?.copy(
|
||||||
|
name = fields.name,
|
||||||
|
urlRule = fields.rule1,
|
||||||
|
showRule = fields.rule2
|
||||||
|
) ?: DictRule(
|
||||||
|
name = fields.name,
|
||||||
|
urlRule = fields.rule1,
|
||||||
|
showRule = fields.rule2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
RuleListScaffold(
|
RuleListScaffold(
|
||||||
title = "字典规则",
|
title = "字典规则",
|
||||||
@@ -310,7 +301,7 @@ fun DictRuleScreen(
|
|||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_str)) },
|
text = stringResource(R.string.import_str),
|
||||||
onClick = { showImportSheet = true; dismiss() },
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import androidx.compose.material3.ExtendedFloatingActionButton
|
|||||||
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.Text
|
||||||
import androidx.compose.material3.WideNavigationRail
|
import androidx.compose.material3.WideNavigationRail
|
||||||
import androidx.compose.material3.WideNavigationRailItem
|
import androidx.compose.material3.WideNavigationRailItem
|
||||||
import androidx.compose.material3.WideNavigationRailValue
|
import androidx.compose.material3.WideNavigationRailValue
|
||||||
@@ -195,7 +196,7 @@ fun MainScreen(
|
|||||||
) { dismiss ->
|
) { dismiss ->
|
||||||
bookshelfUiState.groups.forEachIndexed { groupIndex, group ->
|
bookshelfUiState.groups.forEachIndexed { groupIndex, group ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(group.groupName) },
|
text = group.groupName,
|
||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
if (pagerState.currentPage != index) {
|
if (pagerState.currentPage != index) {
|
||||||
@@ -321,7 +322,7 @@ private fun NavigationIcon(
|
|||||||
val icon = if (selected) destination.m3SelectedIcon else destination.m3Icon
|
val icon = if (selected) destination.m3SelectedIcon else destination.m3Icon
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
if (destination == MainDestination.Bookshelf && upBooksCount > 0) {
|
if (destination == MainDestination.Bookshelf && upBooksCount > 0) {
|
||||||
BadgedBox(badge = { Badge { AppText(upBooksCount.toString()) } }) {
|
BadgedBox(badge = { Badge { Text(upBooksCount.toString()) } }) {
|
||||||
Icon(icon, contentDescription = null)
|
Icon(icon, contentDescription = null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -31,38 +31,37 @@ 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.config.bookshelfConfig.BookshelfConfig
|
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.widget.components.card.GlassCard
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
|
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.CompactSliderSettingItem
|
import io.legado.app.ui.widget.components.settingItem.CompactSliderSettingItem
|
||||||
import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
|
import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BookshelfConfigSheet(
|
fun BookshelfConfigSheet(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||||
|
|
||||||
GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
|
AppModalBottomSheet(
|
||||||
|
title = stringResource(R.string.bookshelf_layout),
|
||||||
|
show = show,
|
||||||
|
onDismissRequest = onDismissRequest
|
||||||
|
) {
|
||||||
|
GlassCard() {
|
||||||
|
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.animateContentSize()
|
.animateContentSize()
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.padding(bottom = 32.dp),
|
.padding(bottom = 32.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
AppText(
|
|
||||||
text = stringResource(R.string.bookshelf_layout),
|
|
||||||
style = LegadoTheme.typography.titleLarge,
|
|
||||||
modifier = Modifier.padding(vertical = 16.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Group Style
|
|
||||||
CompactDropdownSettingItem(
|
CompactDropdownSettingItem(
|
||||||
title = stringResource(R.string.group_style),
|
title = stringResource(R.string.group_style),
|
||||||
selectedValue = BookshelfConfig.bookGroupStyle.toString(),
|
selectedValue = BookshelfConfig.bookGroupStyle.toString(),
|
||||||
|
|||||||
@@ -40,12 +40,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PrimaryScrollableTabRow
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Tab
|
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
||||||
import androidx.compose.material3.pulltorefresh.pullToRefresh
|
import androidx.compose.material3.pulltorefresh.pullToRefresh
|
||||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
@@ -60,13 +57,11 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.ClipEntry
|
import androidx.compose.ui.platform.ClipEntry
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
@@ -86,7 +81,8 @@ import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
|
|||||||
import io.legado.app.ui.widget.components.list.ListScaffold
|
import io.legado.app.ui.widget.components.list.ListScaffold
|
||||||
import io.legado.app.ui.widget.components.menuItem.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.tabRow.AdaptiveTabRow
|
import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
||||||
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import io.legado.app.utils.readText
|
import io.legado.app.utils.readText
|
||||||
import io.legado.app.utils.startActivity
|
import io.legado.app.utils.startActivity
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -237,37 +233,37 @@ fun BookshelfScreen(
|
|||||||
topBarActions = { },
|
topBarActions = { },
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.add_remote_book)) },
|
text = stringResource(R.string.add_remote_book),
|
||||||
onClick = { context.startActivity<RemoteBookActivity>(); dismiss() },
|
onClick = { context.startActivity<RemoteBookActivity>(); dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Wifi, null) }
|
leadingIcon = { Icon(Icons.Default.Wifi, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.book_local)) },
|
text = stringResource(R.string.book_local),
|
||||||
onClick = { context.startActivity<ImportBookActivity>(); dismiss() },
|
onClick = { context.startActivity<ImportBookActivity>(); dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Save, null) }
|
leadingIcon = { Icon(Icons.Default.Save, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.update_toc)) },
|
text = stringResource(R.string.update_toc),
|
||||||
onClick = { viewModel.upToc(uiState.items); dismiss() },
|
onClick = { viewModel.upToc(uiState.items); dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Refresh, null) }
|
leadingIcon = { Icon(Icons.Default.Refresh, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("布局设置") },
|
text = "布局设置",
|
||||||
onClick = { showConfigSheet = true; dismiss() },
|
onClick = { showConfigSheet = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.GridView, null) }
|
leadingIcon = { Icon(Icons.Default.GridView, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.group_manage)) },
|
text = stringResource(R.string.group_manage),
|
||||||
onClick = { showGroupManageSheet = true; dismiss() },
|
onClick = { showGroupManageSheet = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Edit, null) }
|
leadingIcon = { Icon(Icons.Default.Edit, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.add_url)) },
|
text = stringResource(R.string.add_url),
|
||||||
onClick = { showAddUrlDialog = true; dismiss() },
|
onClick = { showAddUrlDialog = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.Link, null) }
|
leadingIcon = { Icon(Icons.Default.Link, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.bookshelf_management)) },
|
text = stringResource(R.string.bookshelf_management),
|
||||||
onClick = {
|
onClick = {
|
||||||
val groupId =
|
val groupId =
|
||||||
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
||||||
@@ -279,7 +275,7 @@ fun BookshelfScreen(
|
|||||||
leadingIcon = { Icon(Icons.Default.Settings, null) }
|
leadingIcon = { Icon(Icons.Default.Settings, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.cache_export)) },
|
text = stringResource(R.string.cache_export),
|
||||||
onClick = {
|
onClick = {
|
||||||
val groupId =
|
val groupId =
|
||||||
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
||||||
@@ -291,7 +287,7 @@ fun BookshelfScreen(
|
|||||||
leadingIcon = { Icon(Icons.Default.Download, null) }
|
leadingIcon = { Icon(Icons.Default.Download, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.export_bookshelf)) },
|
text = stringResource(R.string.export_bookshelf),
|
||||||
onClick = {
|
onClick = {
|
||||||
showExportSheet = true
|
showExportSheet = true
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -299,12 +295,12 @@ fun BookshelfScreen(
|
|||||||
leadingIcon = { Icon(Icons.Default.ImportExport, null) }
|
leadingIcon = { Icon(Icons.Default.ImportExport, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_bookshelf)) },
|
text = stringResource(R.string.import_bookshelf),
|
||||||
onClick = { showImportSheet = true; dismiss() },
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.log)) },
|
text = stringResource(R.string.log),
|
||||||
onClick = {
|
onClick = {
|
||||||
showLogSheet = true
|
showLogSheet = true
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -326,7 +322,7 @@ fun BookshelfScreen(
|
|||||||
uiState.groups.map { it.groupName }
|
uiState.groups.map { it.groupName }
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveTabRow(
|
AppTabRow(
|
||||||
tabTitles = tabTitles,
|
tabTitles = tabTitles,
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = selectedTabIndex,
|
||||||
onTabSelected = { index ->
|
onTabSelected = { index ->
|
||||||
@@ -349,7 +345,7 @@ fun BookshelfScreen(
|
|||||||
) { dismiss ->
|
) { dismiss ->
|
||||||
uiState.groups.forEachIndexed { index, group ->
|
uiState.groups.forEachIndexed { index, group ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(group.groupName) },
|
text = group.groupName,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch { pagerState.animateScrollToPage(index) }
|
scope.launch { pagerState.animateScrollToPage(index) }
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -487,13 +483,15 @@ fun BookshelfScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showConfigSheet) {
|
BookshelfConfigSheet(
|
||||||
BookshelfConfigSheet(onDismissRequest = { showConfigSheet = false })
|
show = showConfigSheet,
|
||||||
}
|
onDismissRequest = { showConfigSheet = false }
|
||||||
|
)
|
||||||
|
|
||||||
if (showGroupManageSheet) {
|
GroupManageSheet(
|
||||||
GroupManageSheet(onDismissRequest = { showGroupManageSheet = false })
|
show = showGroupManageSheet,
|
||||||
}
|
onDismissRequest = { showGroupManageSheet = false }
|
||||||
|
)
|
||||||
|
|
||||||
if (showAddUrlDialog) {
|
if (showAddUrlDialog) {
|
||||||
SourceInputDialog(
|
SourceInputDialog(
|
||||||
@@ -506,40 +504,39 @@ fun BookshelfScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showImportSheet) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showImportSheet,
|
||||||
onDismissRequest = { showImportSheet = false },
|
onDismissRequest = { showImportSheet = false },
|
||||||
title = stringResource(R.string.import_bookshelf),
|
title = stringResource(R.string.import_bookshelf),
|
||||||
onSelectSysFile = { types ->
|
onSelectSysFile = { types ->
|
||||||
importLauncher.launch(types)
|
importLauncher.launch(types)
|
||||||
showImportSheet = false
|
showImportSheet = false
|
||||||
},
|
},
|
||||||
onManualInput = {
|
onManualInput = {
|
||||||
showAddUrlDialog = true
|
showAddUrlDialog = true
|
||||||
showImportSheet = false
|
showImportSheet = false
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json", "txt")
|
allowExtensions = arrayOf("json", "txt")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showExportSheet) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showExportSheet,
|
||||||
onDismissRequest = { showExportSheet = false },
|
onDismissRequest = { showExportSheet = false },
|
||||||
title = stringResource(R.string.export_bookshelf),
|
title = stringResource(R.string.export_bookshelf),
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
exportLauncher.launch("bookshelf.json")
|
exportLauncher.launch("bookshelf.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadBookshelf(uiState.items)
|
viewModel.uploadBookshelf(uiState.items)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showLogSheet) {
|
AppLogSheet(
|
||||||
AppLogSheet(onDismissRequest = { showLogSheet = false })
|
show = showLogSheet,
|
||||||
}
|
onDismissRequest = { showLogSheet = false }
|
||||||
|
)
|
||||||
|
|
||||||
if (uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
Dialog(onDismissRequest = {}) {
|
Dialog(onDismissRequest = {}) {
|
||||||
|
|||||||
@@ -6,18 +6,13 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -26,7 +21,6 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -35,10 +29,9 @@ import io.legado.app.R
|
|||||||
import io.legado.app.data.entities.BookGroup
|
import io.legado.app.data.entities.BookGroup
|
||||||
import io.legado.app.ui.book.group.GroupEditContent
|
import io.legado.app.ui.book.group.GroupEditContent
|
||||||
import io.legado.app.ui.book.group.GroupViewModel
|
import io.legado.app.ui.book.group.GroupViewModel
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||||
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
import io.legado.app.utils.move
|
import io.legado.app.utils.move
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||||
@@ -46,6 +39,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupManageSheet(
|
fun GroupManageSheet(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
viewModel: GroupViewModel = koinViewModel(),
|
viewModel: GroupViewModel = koinViewModel(),
|
||||||
bookshelfViewModel: BookshelfViewModel = koinViewModel()
|
bookshelfViewModel: BookshelfViewModel = koinViewModel()
|
||||||
@@ -78,7 +72,22 @@ fun GroupManageSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
title = if (!isEditing) stringResource(R.string.group_manage) else stringResource(R.string.group_edit),
|
||||||
|
endAction = {
|
||||||
|
if (!isEditing) {
|
||||||
|
SmallIconButton(
|
||||||
|
onClick = {
|
||||||
|
editingGroup = null
|
||||||
|
isEditing = true
|
||||||
|
},
|
||||||
|
icon = Icons.Default.Add
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = isEditing,
|
targetState = isEditing,
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
@@ -93,56 +102,28 @@ fun GroupManageSheet(
|
|||||||
viewModel = viewModel
|
viewModel = viewModel
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Column(
|
LazyColumn(
|
||||||
modifier = Modifier
|
state = listState,
|
||||||
.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
items(listData, key = { it.groupId }) { group ->
|
||||||
modifier = Modifier
|
val manageNameInfo = remember(group) { group.getManageName(context) }
|
||||||
.fillMaxWidth()
|
ReorderableSelectionItem(
|
||||||
.padding(horizontal = 16.dp)
|
state = reorderableState,
|
||||||
.padding(vertical = 8.dp),
|
key = group.groupId,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
|
||||||
verticalAlignment = Alignment.CenterVertically
|
subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
|
||||||
) {
|
isEnabled = group.show,
|
||||||
AppText(
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
text = stringResource(R.string.group_manage),
|
onEnabledChange = { isChecked ->
|
||||||
style = LegadoTheme.typography.titleLarge
|
viewModel.upGroup(group.copy(show = isChecked))
|
||||||
|
},
|
||||||
|
onClickEdit = {
|
||||||
|
editingGroup = group
|
||||||
|
isEditing = true
|
||||||
|
}
|
||||||
)
|
)
|
||||||
IconButton(onClick = {
|
|
||||||
editingGroup = null
|
|
||||||
isEditing = true
|
|
||||||
}) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Add,
|
|
||||||
contentDescription = stringResource(R.string.add)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyColumn(
|
|
||||||
state = listState,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
items(listData, key = { it.groupId }) { group ->
|
|
||||||
val manageNameInfo = remember(group) { group.getManageName(context) }
|
|
||||||
ReorderableSelectionItem(
|
|
||||||
state = reorderableState,
|
|
||||||
key = group.groupId,
|
|
||||||
title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
|
|
||||||
subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
|
|
||||||
isEnabled = group.show,
|
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
|
||||||
onEnabledChange = { isChecked ->
|
|
||||||
viewModel.upGroup(group.copy(show = isChecked))
|
|
||||||
},
|
|
||||||
onClickEdit = {
|
|
||||||
editingGroup = group
|
|
||||||
isEditing = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,13 +153,13 @@ fun ExploreScreen(
|
|||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Group) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Group) },
|
||||||
text = { AppText(stringResource(R.string.all)) },
|
text = stringResource(R.string.all),
|
||||||
onClick = { viewModel.setGroup(""); dismiss() }
|
onClick = { viewModel.setGroup(""); dismiss() }
|
||||||
)
|
)
|
||||||
uiState.groups.forEach { group ->
|
uiState.groups.forEach { group ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Outlined.Label) },
|
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Outlined.Label) },
|
||||||
text = { AppText(group) },
|
text = group,
|
||||||
onClick = { viewModel.setGroup(group); dismiss() }
|
onClick = { viewModel.setGroup(group); dismiss() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -416,29 +416,29 @@ fun ExploreSourceHeader(
|
|||||||
PillHeaderDivider(title = item.bookSourceName)
|
PillHeaderDivider(title = item.bookSourceName)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.VerticalAlignTop) },
|
leadingIcon = { MenuItemIcon(Icons.Default.VerticalAlignTop) },
|
||||||
text = { AppText(stringResource(R.string.to_top)) },
|
text = stringResource(R.string.to_top),
|
||||||
onClick = { onTop(); showMenu = false }
|
onClick = { onTop(); showMenu = false }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Edit) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Edit) },
|
||||||
text = { AppText(stringResource(R.string.edit)) },
|
text = stringResource(R.string.edit),
|
||||||
onClick = { onEdit(); showMenu = false }
|
onClick = { onEdit(); showMenu = false }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Search) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Search) },
|
||||||
text = { AppText(stringResource(R.string.search)) },
|
text = stringResource(R.string.search),
|
||||||
onClick = { onSearch(); showMenu = false }
|
onClick = { onSearch(); showMenu = false }
|
||||||
)
|
)
|
||||||
if (item.hasLoginUrl) {
|
if (item.hasLoginUrl) {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Filled.Login) },
|
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Filled.Login) },
|
||||||
text = { AppText(stringResource(R.string.login)) },
|
text = stringResource(R.string.login),
|
||||||
onClick = { onLogin(); showMenu = false }
|
onClick = { onLogin(); showMenu = false }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Refresh) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Refresh) },
|
||||||
text = { AppText(stringResource(R.string.refresh)) },
|
text = stringResource(R.string.refresh),
|
||||||
onClick = { onRefresh(); showMenu = false }
|
onClick = { onRefresh(); showMenu = false }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
@@ -448,12 +448,8 @@ fun ExploreSourceHeader(
|
|||||||
tint = MaterialTheme.colorScheme.error
|
tint = MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
text = {
|
text = stringResource(R.string.delete),
|
||||||
AppText(
|
color = LegadoTheme.colorScheme.error,
|
||||||
stringResource(R.string.delete),
|
|
||||||
color = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = { onDelete(); showMenu = false }
|
onClick = { onDelete(); showMenu = false }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ fun WebServiceSettingBlock(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp),
|
.padding(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 12.dp),
|
||||||
horizontalArrangement = Arrangement.End
|
horizontalArrangement = Arrangement.End
|
||||||
) {
|
) {
|
||||||
SmallTextButton(
|
SmallTextButton(
|
||||||
|
|||||||
@@ -136,12 +136,12 @@ fun RssScreen(
|
|||||||
dismiss()
|
dismiss()
|
||||||
},
|
},
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Settings) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Settings) },
|
||||||
text = { AppText("订阅源管理") }
|
text = "订阅源管理"
|
||||||
)
|
)
|
||||||
PillDivider()
|
PillDivider()
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Group) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Group) },
|
||||||
text = { AppText(stringResource(R.string.all)) },
|
text = stringResource(R.string.all),
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setGroup("")
|
viewModel.setGroup("")
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -150,7 +150,7 @@ fun RssScreen(
|
|||||||
uiState.groups.forEach { group ->
|
uiState.groups.forEach { group ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Outlined.Label) },
|
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Outlined.Label) },
|
||||||
text = { AppText(group) },
|
text = group,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setGroup(group)
|
viewModel.setGroup(group)
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -247,7 +247,7 @@ fun RssSourceGridItem(
|
|||||||
PillHeaderDivider(title = source.sourceName)
|
PillHeaderDivider(title = source.sourceName)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.VerticalAlignTop) },
|
leadingIcon = { MenuItemIcon(Icons.Default.VerticalAlignTop) },
|
||||||
text = { AppText(stringResource(R.string.to_top)) },
|
text = stringResource(R.string.to_top),
|
||||||
onClick = {
|
onClick = {
|
||||||
onTop()
|
onTop()
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -255,7 +255,7 @@ fun RssSourceGridItem(
|
|||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Edit) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Edit) },
|
||||||
text = { AppText(stringResource(R.string.edit)) },
|
text = stringResource(R.string.edit),
|
||||||
onClick = {
|
onClick = {
|
||||||
onEdit()
|
onEdit()
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -264,7 +264,7 @@ fun RssSourceGridItem(
|
|||||||
if (!source.loginUrl.isNullOrBlank()) {
|
if (!source.loginUrl.isNullOrBlank()) {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Filled.Login) },
|
leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Filled.Login) },
|
||||||
text = { AppText(stringResource(R.string.login)) },
|
text = stringResource(R.string.login),
|
||||||
onClick = {
|
onClick = {
|
||||||
onLogin()
|
onLogin()
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -273,7 +273,7 @@ fun RssSourceGridItem(
|
|||||||
}
|
}
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
leadingIcon = { MenuItemIcon(Icons.Default.Close) },
|
leadingIcon = { MenuItemIcon(Icons.Default.Close) },
|
||||||
text = { AppText(stringResource(R.string.disable_source)) },
|
text = stringResource(R.string.disable_source),
|
||||||
onClick = {
|
onClick = {
|
||||||
onDisable()
|
onDisable()
|
||||||
showMenu = false
|
showMenu = false
|
||||||
@@ -286,12 +286,8 @@ fun RssSourceGridItem(
|
|||||||
tint = MaterialTheme.colorScheme.error
|
tint = MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
text = {
|
text = stringResource(R.string.delete),
|
||||||
AppText(
|
color = MaterialTheme.colorScheme.error,
|
||||||
stringResource(R.string.delete),
|
|
||||||
color = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = {
|
onClick = {
|
||||||
onDelete()
|
onDelete()
|
||||||
showMenu = false
|
showMenu = false
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import androidx.compose.animation.SharedTransitionScope
|
|||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -19,7 +18,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.FileOpen
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
@@ -57,7 +55,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.data.entities.ReplaceRule
|
import io.legado.app.data.entities.ReplaceRule
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
|
||||||
import io.legado.app.ui.widget.components.ActionItem
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||||
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
||||||
@@ -70,7 +67,7 @@ import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
|||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||||
import io.legado.app.ui.widget.components.tabRow.AdaptiveTabRow
|
import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
@@ -161,56 +158,48 @@ fun ReplaceRuleScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showImportSheet) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showImportSheet,
|
||||||
onDismissRequest = { showImportSheet = false },
|
onDismissRequest = { showImportSheet = false },
|
||||||
title = stringResource(R.string.import_replace_rule),
|
title = stringResource(R.string.import_replace_rule),
|
||||||
onSelectSysFile = { types ->
|
onSelectSysFile = { types ->
|
||||||
importDoc.launch(types)
|
importDoc.launch(types)
|
||||||
showImportSheet = false
|
showImportSheet = false
|
||||||
},
|
},
|
||||||
onManualInput = {
|
onManualInput = {
|
||||||
showUrlInput = true
|
showUrlInput = true
|
||||||
showImportSheet = false
|
showImportSheet = false
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json", "txt")
|
allowExtensions = arrayOf("json", "txt")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showExportSheet) {
|
FilePickerSheet(
|
||||||
FilePickerSheet(
|
show = showExportSheet,
|
||||||
onDismissRequest = { showExportSheet = false },
|
onDismissRequest = { showExportSheet = false },
|
||||||
onSelectSysDir = {
|
onSelectSysDir = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
exportDoc.launch("exportReplaceRule.json")
|
exportDoc.launch("exportReplaceRule.json")
|
||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
allowExtensions = arrayOf("json")
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
(importState as? BaseImportUiState.Success<ReplaceRule>)?.let { state ->
|
BatchImportDialog(
|
||||||
BatchImportDialog(
|
title = stringResource(R.string.import_replace_rule),
|
||||||
title = stringResource(R.string.import_replace_rule),
|
importState = importState,
|
||||||
importState = state,
|
onDismissRequest = { viewModel.cancelImport() },
|
||||||
onDismissRequest = { viewModel.cancelImport() },
|
onToggleItem = { viewModel.toggleImportSelection(it) },
|
||||||
onToggleItem = { viewModel.toggleImportSelection(it) },
|
onToggleAll = { viewModel.toggleImportAll(it) },
|
||||||
onToggleAll = { viewModel.toggleImportAll(it) },
|
onConfirm = { viewModel.saveImportedRules() },
|
||||||
onConfirm = { viewModel.saveImportedRules() },
|
topBarActions = {},
|
||||||
topBarActions = {},
|
itemTitle = { rule -> rule.name },
|
||||||
itemContent = { rule, _ ->
|
itemSubtitle = { rule ->
|
||||||
Column {
|
rule.group?.takeIf { it.isNotBlank() }
|
||||||
AppText(rule.name, style = LegadoTheme.typography.titleMedium)
|
}
|
||||||
if (!rule.group.isNullOrBlank()) {
|
)
|
||||||
AppText(rule.group!!, style = LegadoTheme.typography.bodySmall)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importState is BaseImportUiState.Loading) {
|
if (importState is BaseImportUiState.Loading) {
|
||||||
Dialog(onDismissRequest = { viewModel.cancelImport() }) { LoadingIndicator() }
|
Dialog(onDismissRequest = { viewModel.cancelImport() }) { LoadingIndicator() }
|
||||||
@@ -263,14 +252,15 @@ fun ReplaceRuleScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showGroupManageSheet) {
|
|
||||||
GroupManageBottomSheet(
|
GroupManageBottomSheet(
|
||||||
groups = groups,
|
show = showGroupManageSheet,
|
||||||
onDismissRequest = { showGroupManageSheet = false },
|
groups = groups,
|
||||||
onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
|
onDismissRequest = { showGroupManageSheet = false },
|
||||||
onDeleteGroup = { viewModel.delGroup(it) }
|
onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
|
||||||
)
|
onDeleteGroup = { viewModel.delGroup(it) }
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
showDeleteRuleDialog?.let { rule ->
|
showDeleteRuleDialog?.let { rule ->
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -343,7 +333,7 @@ fun ReplaceRuleScreen(
|
|||||||
},
|
},
|
||||||
bottomContent = {
|
bottomContent = {
|
||||||
if (tabItems.size > 1) {
|
if (tabItems.size > 1) {
|
||||||
AdaptiveTabRow(
|
AppTabRow(
|
||||||
tabTitles = tabItems,
|
tabTitles = tabItems,
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = selectedTabIndex,
|
||||||
onTabSelected = { index ->
|
onTabSelected = { index ->
|
||||||
@@ -384,29 +374,29 @@ fun ReplaceRuleScreen(
|
|||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_str)) },
|
text = stringResource(R.string.import_str),
|
||||||
onClick = { showImportSheet = true; dismiss() },
|
onClick = { showImportSheet = true; dismiss() },
|
||||||
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
leadingIcon = { Icon(Icons.Default.FileOpen, null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("分组管理") },
|
text = "分组管理",
|
||||||
onClick = { showGroupManageSheet = true; dismiss() }
|
onClick = { showGroupManageSheet = true; dismiss() }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("帮助") },
|
text = "帮助",
|
||||||
onClick = { /*TODO*/ dismiss() }
|
onClick = { /*TODO*/ dismiss() }
|
||||||
)
|
)
|
||||||
PillDivider()
|
PillDivider()
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("旧的在前") },
|
text = "旧的在前",
|
||||||
onClick = { viewModel.setSortMode("asc"); dismiss() }
|
onClick = { viewModel.setSortMode("asc"); dismiss() }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("新的在前") },
|
text = "新的在前",
|
||||||
onClick = { viewModel.setSortMode("desc"); dismiss() }
|
onClick = { viewModel.setSortMode("desc"); dismiss() }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("名称升序") },
|
text = "名称升序",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setSortMode("name_asc")
|
viewModel.setSortMode("name_asc")
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -416,7 +406,7 @@ fun ReplaceRuleScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("名称降序") },
|
text = "名称降序",
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setSortMode("name_desc")
|
viewModel.setSortMode("name_desc")
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -473,16 +463,16 @@ fun ReplaceRuleScreen(
|
|||||||
//clipInOverlayDuringTransition = OverlayClip(RoundedCornerShape(12.dp))
|
//clipInOverlayDuringTransition = OverlayClip(RoundedCornerShape(12.dp))
|
||||||
),
|
),
|
||||||
dropdownContent = { dismiss ->
|
dropdownContent = { dismiss ->
|
||||||
DropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("移至顶部") },
|
text = "移至顶部",
|
||||||
onClick = { viewModel.toTop(ui.rule); dismiss() }
|
onClick = { viewModel.toTop(ui.rule); dismiss() }
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("移至底部") },
|
text = "移至底部",
|
||||||
onClick = { viewModel.toBottom(ui.rule); dismiss() }
|
onClick = { viewModel.toBottom(ui.rule); dismiss() }
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText("删除") },
|
text = "删除",
|
||||||
onClick = { showDeleteRuleDialog = ui.rule; dismiss() }
|
onClick = { showDeleteRuleDialog = ui.rule; dismiss() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package io.legado.app.ui.rss.favorites
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -44,7 +42,6 @@ import io.legado.app.ui.widget.components.dialog.TextListInputDialog
|
|||||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
import io.legado.app.ui.widget.components.rules.RuleListScaffold
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
import io.legado.app.utils.startActivity
|
import io.legado.app.utils.startActivity
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -129,11 +126,10 @@ fun RssFavoritesScreen(
|
|||||||
topBarActions = {},
|
topBarActions = {},
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = {
|
text = stringResource(R.string.all),
|
||||||
|
leadingIcon = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(Icons.Default.Group, null, modifier = Modifier.size(18.dp))
|
Icon(Icons.Default.Group, null, modifier = Modifier.size(18.dp))
|
||||||
Spacer(Modifier.width(12.dp))
|
|
||||||
AppText(stringResource(R.string.all))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -143,15 +139,14 @@ fun RssFavoritesScreen(
|
|||||||
)
|
)
|
||||||
groups.forEach { group ->
|
groups.forEach { group ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = {
|
text = group,
|
||||||
|
leadingIcon = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Outlined.Label,
|
Icons.AutoMirrored.Outlined.Label,
|
||||||
null,
|
null,
|
||||||
modifier = Modifier.size(18.dp)
|
modifier = Modifier.size(18.dp)
|
||||||
)
|
)
|
||||||
Spacer(Modifier.width(12.dp))
|
|
||||||
AppText(group)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -162,11 +157,10 @@ fun RssFavoritesScreen(
|
|||||||
}
|
}
|
||||||
PillDivider()
|
PillDivider()
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = {
|
text = stringResource(R.string.delete_select_group),
|
||||||
|
leadingIcon = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(Icons.Default.DeleteSweep, null, modifier = Modifier.size(18.dp))
|
Icon(Icons.Default.DeleteSweep, null, modifier = Modifier.size(18.dp))
|
||||||
Spacer(Modifier.width(12.dp))
|
|
||||||
AppText(stringResource(R.string.delete_select_group))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -175,11 +169,10 @@ fun RssFavoritesScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = {
|
text = stringResource(R.string.all),
|
||||||
|
leadingIcon = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(Icons.Default.DeleteForever, null, modifier = Modifier.size(18.dp))
|
Icon(Icons.Default.DeleteForever, null, modifier = Modifier.size(18.dp))
|
||||||
Spacer(Modifier.width(12.dp))
|
|
||||||
AppText(stringResource(R.string.all))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -248,14 +241,14 @@ fun RssFavoritesScreen(
|
|||||||
},
|
},
|
||||||
dropdownContent = { dismiss ->
|
dropdownContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.change_group)) },
|
text = stringResource(R.string.change_group),
|
||||||
onClick = {
|
onClick = {
|
||||||
showSetGroupDialog = rssStar
|
showSetGroupDialog = rssStar
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.delete)) },
|
text = stringResource(R.string.delete),
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.deleteStar(rssStar)
|
viewModel.deleteStar(rssStar)
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -41,7 +40,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.data.entities.RssSource
|
import io.legado.app.data.entities.RssSource
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
|
||||||
import io.legado.app.ui.widget.components.ActionItem
|
import io.legado.app.ui.widget.components.ActionItem
|
||||||
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
import io.legado.app.ui.widget.components.DraggableSelectionHandler
|
||||||
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
import io.legado.app.ui.widget.components.GroupManageBottomSheet
|
||||||
@@ -50,7 +48,6 @@ import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
|
|||||||
import io.legado.app.ui.widget.components.dialog.TextListInputDialog
|
import io.legado.app.ui.widget.components.dialog.TextListInputDialog
|
||||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
|
|
||||||
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
|
||||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||||
@@ -175,50 +172,41 @@ fun RssSourceScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showGroupManageSheet) {
|
GroupManageBottomSheet(
|
||||||
GroupManageBottomSheet(
|
show = showGroupManageSheet,
|
||||||
groups = groups,
|
groups = groups,
|
||||||
onDismissRequest = { showGroupManageSheet = false },
|
onDismissRequest = { showGroupManageSheet = false },
|
||||||
onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
|
onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
|
||||||
onDeleteGroup = { viewModel.delGroup(it) }
|
onDeleteGroup = { viewModel.delGroup(it) }
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (showFilePickerSheet) {
|
|
||||||
FilePickerSheet(
|
|
||||||
onDismissRequest = { showFilePickerSheet = false },
|
|
||||||
onSelectSysDir = {
|
|
||||||
showFilePickerSheet = false
|
|
||||||
exportDoc.launch("exportRssSource.json")
|
|
||||||
},
|
|
||||||
onUpload = {
|
|
||||||
showFilePickerSheet = false
|
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
|
||||||
},
|
|
||||||
allowExtensions = arrayOf("json")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
(importState as? BaseImportUiState.Success<RssSource>)?.let { state ->
|
FilePickerSheet(
|
||||||
BatchImportDialog(
|
show = showFilePickerSheet,
|
||||||
title = stringResource(R.string.import_rss_source),
|
onDismissRequest = { showFilePickerSheet = false },
|
||||||
importState = state,
|
onSelectSysDir = {
|
||||||
onDismissRequest = { viewModel.cancelImport() },
|
showFilePickerSheet = false
|
||||||
onToggleItem = { viewModel.toggleImportSelection(it) },
|
exportDoc.launch("exportRssSource.json")
|
||||||
onToggleAll = { viewModel.toggleImportAll(it) },
|
},
|
||||||
onConfirm = { viewModel.saveImportedRules() },
|
onUpload = {
|
||||||
itemContent = { source, _ ->
|
showFilePickerSheet = false
|
||||||
Column {
|
viewModel.uploadSelectedRules(selectedIds, rules)
|
||||||
AppText(source.sourceName, style = LegadoTheme.typography.titleMedium)
|
},
|
||||||
AppText(
|
allowExtensions = arrayOf("json")
|
||||||
source.sourceUrl,
|
)
|
||||||
style = LegadoTheme.typography.bodySmall,
|
|
||||||
maxLines = 1
|
BatchImportDialog(
|
||||||
)
|
title = stringResource(R.string.import_rss_source),
|
||||||
}
|
importState = importState,
|
||||||
}
|
onDismissRequest = { viewModel.cancelImport() },
|
||||||
)
|
onToggleItem = { viewModel.toggleImportSelection(it) },
|
||||||
}
|
onToggleAll = { viewModel.toggleImportAll(it) },
|
||||||
|
onConfirm = { viewModel.saveImportedRules() },
|
||||||
|
itemTitle = { rule -> rule.sourceName },
|
||||||
|
itemSubtitle = { rule ->
|
||||||
|
rule.sourceUrl.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
||||||
if (!reorderableState.isAnyItemDragging) {
|
if (!reorderableState.isAnyItemDragging) {
|
||||||
@@ -291,11 +279,11 @@ fun RssSourceScreen(
|
|||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
onClick = { showGroupManageSheet = true },
|
onClick = { showGroupManageSheet = true },
|
||||||
text = { AppText("分组管理") },
|
text = "分组管理",
|
||||||
)
|
)
|
||||||
Box {
|
Box {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_rss_source)) },
|
text = stringResource(R.string.import_rss_source),
|
||||||
onClick = { showImportMenu = true }
|
onClick = { showImportMenu = true }
|
||||||
)
|
)
|
||||||
RoundDropdownMenu(
|
RoundDropdownMenu(
|
||||||
@@ -303,7 +291,7 @@ fun RssSourceScreen(
|
|||||||
onDismissRequest = { showImportMenu = false }
|
onDismissRequest = { showImportMenu = false }
|
||||||
) {
|
) {
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_on_line)) },
|
text = stringResource(R.string.import_on_line),
|
||||||
onClick = {
|
onClick = {
|
||||||
showImportMenu = false
|
showImportMenu = false
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -311,7 +299,7 @@ fun RssSourceScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_local)) },
|
text = stringResource(R.string.import_local),
|
||||||
onClick = {
|
onClick = {
|
||||||
showImportMenu = false
|
showImportMenu = false
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -319,7 +307,7 @@ fun RssSourceScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.import_default_rule)) },
|
text = stringResource(R.string.import_default_rule),
|
||||||
onClick = {
|
onClick = {
|
||||||
showImportMenu = false
|
showImportMenu = false
|
||||||
dismiss()
|
dismiss()
|
||||||
@@ -330,29 +318,29 @@ fun RssSourceScreen(
|
|||||||
}
|
}
|
||||||
PillDivider()
|
PillDivider()
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.all)) },
|
text = stringResource(R.string.all),
|
||||||
onClick = { dismiss(); viewModel.setGroupFilter(null) }
|
onClick = { dismiss(); viewModel.setGroupFilter(null) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.enabled)) },
|
text = stringResource(R.string.enabled),
|
||||||
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_ENABLED) }
|
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_ENABLED) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.disabled)) },
|
text = stringResource(R.string.disabled),
|
||||||
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_DISABLED) }
|
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_DISABLED) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.need_login)) },
|
text = stringResource(R.string.need_login),
|
||||||
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_LOGIN) }
|
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_LOGIN) }
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.no_group)) },
|
text = stringResource(R.string.no_group),
|
||||||
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_NO_GROUP) }
|
onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_NO_GROUP) }
|
||||||
)
|
)
|
||||||
PillDivider()
|
PillDivider()
|
||||||
groups.forEach { group ->
|
groups.forEach { group ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(group) },
|
text = group,
|
||||||
onClick = { dismiss(); viewModel.setGroupFilter("${RssSourceViewModel.PREFIX_GROUP}$group") }
|
onClick = { dismiss(); viewModel.setGroupFilter("${RssSourceViewModel.PREFIX_GROUP}$group") }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ fun RuleSubScreen(
|
|||||||
topBarActions = {},
|
topBarActions = {},
|
||||||
dropDownMenuContent = { dismiss ->
|
dropDownMenuContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = {
|
text = stringResource(R.string.sort),
|
||||||
|
leadingIcon = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(Icons.AutoMirrored.Filled.Sort, null, modifier = Modifier.size(18.dp))
|
Icon(Icons.AutoMirrored.Filled.Sort, null, modifier = Modifier.size(18.dp))
|
||||||
Spacer(Modifier.size(12.dp))
|
Spacer(Modifier.size(12.dp))
|
||||||
@@ -185,7 +186,7 @@ fun RuleSubScreen(
|
|||||||
},
|
},
|
||||||
dropdownContent = { dismiss ->
|
dropdownContent = { dismiss ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(stringResource(R.string.delete)) },
|
text = stringResource(R.string.delete),
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.delete(ruleSub)
|
viewModel.delete(ruleSub)
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
import top.yukonga.miuix.kmp.theme.ThemeController
|
import top.yukonga.miuix.kmp.theme.ThemeController
|
||||||
@@ -46,8 +47,15 @@ fun AppTheme(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val themeColors = remember(colorScheme, darkTheme, seedColor, paletteStyle, colorSchemeMode) {
|
val themeColors = remember(
|
||||||
LegadoThemeColors(
|
colorScheme,
|
||||||
|
darkTheme,
|
||||||
|
seedColor,
|
||||||
|
paletteStyle,
|
||||||
|
colorSchemeMode,
|
||||||
|
composeEngine
|
||||||
|
) {
|
||||||
|
LegadoThemeMode(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
isDark = darkTheme,
|
isDark = darkTheme,
|
||||||
seedColor = seedColor,
|
seedColor = seedColor,
|
||||||
@@ -61,43 +69,228 @@ fun AppTheme(
|
|||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalLegadoThemeColors provides themeColors
|
LocalLegadoThemeColors provides themeColors
|
||||||
) {
|
) {
|
||||||
when {
|
if (ThemeResolver.isMiuixEngine(themeColors.composeEngine)) {
|
||||||
ThemeResolver.isMiuixEngine(themeColors.composeEngine) -> {
|
val controller = remember(colorSchemeMode, darkTheme) {
|
||||||
val controller = remember(colorSchemeMode, darkTheme) {
|
ThemeController(colorSchemeMode = colorSchemeMode, isDark = darkTheme)
|
||||||
ThemeController(
|
}
|
||||||
colorSchemeMode = colorSchemeMode,
|
|
||||||
isDark = darkTheme
|
MiuixTheme(controller = controller) {
|
||||||
|
val miuixStyles = MiuixTheme.textStyles
|
||||||
|
val legadoTypography = remember(miuixStyles) {
|
||||||
|
LegadoTypography(
|
||||||
|
headlineLarge = miuixStyles.title1,
|
||||||
|
headlineLargeEmphasized = miuixStyles.title1.copy(fontWeight = FontWeight.Medium),
|
||||||
|
headlineMedium = miuixStyles.title2,
|
||||||
|
headlineMediumEmphasized = miuixStyles.title2.copy(fontWeight = FontWeight.Medium),
|
||||||
|
headlineSmall = miuixStyles.title3,
|
||||||
|
headlineSmallEmphasized = miuixStyles.title3.copy(fontWeight = FontWeight.Medium),
|
||||||
|
|
||||||
|
|
||||||
|
titleLarge = miuixStyles.headline1,
|
||||||
|
titleLargeEmphasized = miuixStyles.headline1.copy(fontWeight = FontWeight.Medium),
|
||||||
|
titleMedium = miuixStyles.headline2,
|
||||||
|
titleMediumEmphasized = miuixStyles.headline2.copy(fontWeight = FontWeight.Medium),
|
||||||
|
titleSmall = miuixStyles.subtitle,
|
||||||
|
titleSmallEmphasized = miuixStyles.subtitle.copy(fontWeight = FontWeight.Medium),
|
||||||
|
|
||||||
|
|
||||||
|
bodyLarge = miuixStyles.paragraph,
|
||||||
|
bodyLargeEmphasized = miuixStyles.paragraph.copy(fontWeight = FontWeight.Medium),
|
||||||
|
bodyMedium = miuixStyles.body1,
|
||||||
|
bodyMediumEmphasized = miuixStyles.body1.copy(fontWeight = FontWeight.Medium),
|
||||||
|
bodySmall = miuixStyles.body2,
|
||||||
|
bodySmallEmphasized = miuixStyles.body2.copy(fontWeight = FontWeight.Medium),
|
||||||
|
|
||||||
|
labelLarge = miuixStyles.button,
|
||||||
|
labelLargeEmphasized = miuixStyles.button.copy(fontWeight = FontWeight.Medium),
|
||||||
|
labelMedium = miuixStyles.footnote1,
|
||||||
|
labelMediumEmphasized = miuixStyles.footnote1.copy(fontWeight = FontWeight.Medium),
|
||||||
|
labelSmall = miuixStyles.footnote2,
|
||||||
|
labelSmallEmphasized = miuixStyles.footnote2.copy(fontWeight = FontWeight.Medium)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
MiuixTheme(
|
val miuixColorScheme = MiuixTheme.colorScheme
|
||||||
controller = controller
|
|
||||||
) {
|
|
||||||
val miuixTextStyles = MiuixTheme.textStyles
|
|
||||||
val mappedTypography = remember(miuixTextStyles) {
|
|
||||||
miuixStylesToM3Typography(miuixTextStyles)
|
|
||||||
}
|
|
||||||
|
|
||||||
CompositionLocalProvider(
|
val mappedColorScheme = remember(miuixColorScheme) {
|
||||||
LocalLegadoTypography provides mappedTypography
|
LegadoColorScheme(
|
||||||
) {
|
primary = miuixColorScheme.primary,
|
||||||
AppBackground(darkTheme = darkTheme) {
|
onPrimary = miuixColorScheme.onPrimary,
|
||||||
content()
|
primaryContainer = miuixColorScheme.primaryContainer,
|
||||||
}
|
onPrimaryContainer = miuixColorScheme.onPrimaryContainer,
|
||||||
}
|
inversePrimary = miuixColorScheme.primaryVariant,
|
||||||
|
|
||||||
|
secondary = miuixColorScheme.primary.copy(alpha = 0.5f),
|
||||||
|
onSecondary = miuixColorScheme.onPrimary.copy(alpha = 0.5f),
|
||||||
|
secondaryContainer = miuixColorScheme.primaryContainer.copy(alpha = 0.5f),
|
||||||
|
onSecondaryContainer = miuixColorScheme.onPrimaryContainer.copy(alpha = 0.5f),
|
||||||
|
|
||||||
|
tertiary = miuixColorScheme.secondary,
|
||||||
|
onTertiary = miuixColorScheme.onSecondary,
|
||||||
|
tertiaryContainer = miuixColorScheme.secondaryContainer,
|
||||||
|
onTertiaryContainer = miuixColorScheme.onSecondaryContainer,
|
||||||
|
|
||||||
|
// ================= 4. 背景与表面 (Background & Surface) =================
|
||||||
|
background = miuixColorScheme.background,
|
||||||
|
onBackground = miuixColorScheme.onBackground,
|
||||||
|
|
||||||
|
surface = miuixColorScheme.surface,
|
||||||
|
onSurface = miuixColorScheme.onSurface,
|
||||||
|
surfaceVariant = miuixColorScheme.surfaceVariant,
|
||||||
|
// M3 的 onSurfaceVariant 通常是次级文字色。Miuix 的 onSurfaceSecondary 完美契合这个语义
|
||||||
|
onSurfaceVariant = miuixColorScheme.onSurfaceSecondary,
|
||||||
|
|
||||||
|
// M3 中用于给 Surface 叠加一层极淡主题色的属性,通常直接取 primary
|
||||||
|
surfaceTint = miuixColorScheme.primary,
|
||||||
|
|
||||||
|
// Inverse 系列通常用于深色模式下的反色提示(如 Snackbar)。
|
||||||
|
// 简单映射法:直接用现有的 onSurface 和 surface 交叉互换。
|
||||||
|
inverseSurface = miuixColorScheme.onSurface,
|
||||||
|
inverseOnSurface = miuixColorScheme.surface,
|
||||||
|
|
||||||
|
// ================= 5. 错误状态 (Error) =================
|
||||||
|
error = miuixColorScheme.error,
|
||||||
|
onError = miuixColorScheme.onError,
|
||||||
|
errorContainer = miuixColorScheme.errorContainer,
|
||||||
|
onErrorContainer = miuixColorScheme.onErrorContainer,
|
||||||
|
|
||||||
|
// ================= 6. 边框、分割线与遮罩 (Outline & Scrim) =================
|
||||||
|
outline = miuixColorScheme.outline,
|
||||||
|
// outlineVariant 在 M3 中常用于分割线。Miuix 刚好有 dividerLine
|
||||||
|
outlineVariant = miuixColorScheme.dividerLine,
|
||||||
|
// scrim 是 M3 的遮罩层(如弹窗背后的阴影)。Miuix 刚好有 windowDimming
|
||||||
|
scrim = miuixColorScheme.windowDimming,
|
||||||
|
|
||||||
|
surfaceBright = miuixColorScheme.surface, // Miuix 缺省,用 surface 兜底
|
||||||
|
surfaceDim = miuixColorScheme.background, // Miuix 缺省,用 background 兜底
|
||||||
|
surfaceContainer = miuixColorScheme.surfaceContainer,
|
||||||
|
surfaceContainerHigh = miuixColorScheme.surfaceContainerHigh,
|
||||||
|
surfaceContainerHighest = miuixColorScheme.surfaceContainerHighest,
|
||||||
|
surfaceContainerLow = miuixColorScheme.secondaryContainer,
|
||||||
|
surfaceContainerLowest = miuixColorScheme.background,
|
||||||
|
|
||||||
|
primaryFixed = miuixColorScheme.primaryContainer,
|
||||||
|
primaryFixedDim = miuixColorScheme.primary,
|
||||||
|
onPrimaryFixed = miuixColorScheme.onPrimaryContainer,
|
||||||
|
onPrimaryFixedVariant = miuixColorScheme.onPrimary,
|
||||||
|
secondaryFixed = miuixColorScheme.secondaryContainer,
|
||||||
|
secondaryFixedDim = miuixColorScheme.secondary,
|
||||||
|
onSecondaryFixed = miuixColorScheme.onSecondaryContainer,
|
||||||
|
onSecondaryFixedVariant = miuixColorScheme.onSecondary,
|
||||||
|
tertiaryFixed = miuixColorScheme.tertiaryContainer,
|
||||||
|
tertiaryFixedDim = miuixColorScheme.tertiaryContainerVariant,
|
||||||
|
onTertiaryFixed = miuixColorScheme.onTertiaryContainer,
|
||||||
|
onTertiaryFixedVariant = miuixColorScheme.onTertiaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalLegadoTypography provides legadoTypography,
|
||||||
|
LocalLegadoColorScheme provides mappedColorScheme
|
||||||
|
) {
|
||||||
|
AppBackground(darkTheme = darkTheme) { content() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
val Typography = Typography()
|
||||||
|
MaterialExpressiveTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography,
|
||||||
|
motionScheme = MotionScheme.expressive(),
|
||||||
|
shapes = Shapes()
|
||||||
|
) {
|
||||||
|
val legadoTypography = remember(Typography) {
|
||||||
|
LegadoTypography(
|
||||||
|
headlineLarge = Typography.headlineLarge,
|
||||||
|
headlineLargeEmphasized = Typography.headlineLargeEmphasized,
|
||||||
|
headlineMedium = Typography.headlineMedium,
|
||||||
|
headlineMediumEmphasized = Typography.headlineMediumEmphasized,
|
||||||
|
headlineSmall = Typography.headlineSmall,
|
||||||
|
headlineSmallEmphasized = Typography.headlineSmallEmphasized,
|
||||||
|
|
||||||
else -> {
|
|
||||||
MaterialExpressiveTheme(
|
titleLarge = Typography.titleLarge,
|
||||||
colorScheme = colorScheme,
|
titleLargeEmphasized = Typography.titleLargeEmphasized,
|
||||||
typography = Typography(),
|
titleMedium = Typography.titleMedium,
|
||||||
motionScheme = MotionScheme.expressive(),
|
titleMediumEmphasized = Typography.titleMediumEmphasized,
|
||||||
shapes = Shapes()
|
titleSmall = Typography.titleSmall,
|
||||||
|
titleSmallEmphasized = Typography.titleSmallEmphasized,
|
||||||
|
|
||||||
|
|
||||||
|
bodyLarge = Typography.bodyLarge,
|
||||||
|
bodyLargeEmphasized = Typography.bodyLargeEmphasized,
|
||||||
|
bodyMedium = Typography.bodyMedium,
|
||||||
|
bodyMediumEmphasized = Typography.bodyMediumEmphasized,
|
||||||
|
bodySmall = Typography.bodySmall,
|
||||||
|
bodySmallEmphasized = Typography.bodySmallEmphasized,
|
||||||
|
|
||||||
|
labelLarge = Typography.labelLarge,
|
||||||
|
labelLargeEmphasized = Typography.labelLargeEmphasized,
|
||||||
|
labelMedium = Typography.labelMedium,
|
||||||
|
labelMediumEmphasized = Typography.labelMediumEmphasized,
|
||||||
|
labelSmall = Typography.labelSmall,
|
||||||
|
labelSmallEmphasized = Typography.labelSmallEmphasized
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val semanticColors = remember(colorScheme) {
|
||||||
|
LegadoColorScheme(
|
||||||
|
primary = colorScheme.primary,
|
||||||
|
onPrimary = colorScheme.onPrimary,
|
||||||
|
primaryContainer = colorScheme.primaryContainer,
|
||||||
|
onPrimaryContainer = colorScheme.onPrimaryContainer,
|
||||||
|
inversePrimary = colorScheme.inversePrimary,
|
||||||
|
secondary = colorScheme.secondary,
|
||||||
|
onSecondary = colorScheme.onSecondary,
|
||||||
|
secondaryContainer = colorScheme.secondaryContainer,
|
||||||
|
onSecondaryContainer = colorScheme.onSecondaryContainer,
|
||||||
|
tertiary = colorScheme.tertiary,
|
||||||
|
onTertiary = colorScheme.onTertiary,
|
||||||
|
tertiaryContainer = colorScheme.tertiaryContainer,
|
||||||
|
onTertiaryContainer = colorScheme.onTertiaryContainer,
|
||||||
|
background = colorScheme.background,
|
||||||
|
onBackground = colorScheme.onBackground,
|
||||||
|
surface = colorScheme.surface,
|
||||||
|
onSurface = colorScheme.onSurface,
|
||||||
|
surfaceVariant = colorScheme.surfaceVariant,
|
||||||
|
onSurfaceVariant = colorScheme.onSurfaceVariant,
|
||||||
|
surfaceTint = colorScheme.surfaceTint,
|
||||||
|
inverseSurface = colorScheme.inverseSurface,
|
||||||
|
inverseOnSurface = colorScheme.inverseOnSurface,
|
||||||
|
error = colorScheme.error,
|
||||||
|
onError = colorScheme.onError,
|
||||||
|
errorContainer = colorScheme.errorContainer,
|
||||||
|
onErrorContainer = colorScheme.onErrorContainer,
|
||||||
|
outline = colorScheme.outline,
|
||||||
|
outlineVariant = colorScheme.outlineVariant,
|
||||||
|
scrim = colorScheme.scrim,
|
||||||
|
surfaceBright = colorScheme.surfaceBright,
|
||||||
|
surfaceDim = colorScheme.surfaceDim,
|
||||||
|
surfaceContainer = colorScheme.surfaceContainer,
|
||||||
|
surfaceContainerHigh = colorScheme.surfaceContainerHigh,
|
||||||
|
surfaceContainerHighest = colorScheme.surfaceContainerHighest,
|
||||||
|
surfaceContainerLow = colorScheme.surfaceContainerLow,
|
||||||
|
surfaceContainerLowest = colorScheme.surfaceContainerLowest,
|
||||||
|
primaryFixed = colorScheme.primaryFixed,
|
||||||
|
primaryFixedDim = colorScheme.primaryFixedDim,
|
||||||
|
onPrimaryFixed = colorScheme.onPrimaryFixed,
|
||||||
|
onPrimaryFixedVariant = colorScheme.onPrimaryFixedVariant,
|
||||||
|
secondaryFixed = colorScheme.secondaryFixed,
|
||||||
|
secondaryFixedDim = colorScheme.secondaryFixedDim,
|
||||||
|
onSecondaryFixed = colorScheme.onSecondaryFixed,
|
||||||
|
onSecondaryFixedVariant = colorScheme.onSecondaryFixedVariant,
|
||||||
|
tertiaryFixed = colorScheme.tertiaryFixed,
|
||||||
|
tertiaryFixedDim = colorScheme.tertiaryFixedDim,
|
||||||
|
onTertiaryFixed = colorScheme.onTertiaryFixed,
|
||||||
|
onTertiaryFixedVariant = colorScheme.onTertiaryFixedVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalLegadoTypography provides legadoTypography,
|
||||||
|
LocalLegadoColorScheme provides semanticColors
|
||||||
) {
|
) {
|
||||||
AppBackground(darkTheme = darkTheme) {
|
AppBackground(darkTheme = darkTheme) { content() }
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package io.legado.app.ui.theme
|
package io.legado.app.ui.theme
|
||||||
|
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.Typography
|
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
import com.materialkolor.PaletteStyle
|
import com.materialkolor.PaletteStyle
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
|
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
|
||||||
|
|
||||||
data class LegadoThemeColors(
|
data class LegadoThemeMode(
|
||||||
val colorScheme: ColorScheme,
|
val colorScheme: ColorScheme,
|
||||||
val isDark: Boolean,
|
val isDark: Boolean,
|
||||||
val seedColor: Color,
|
val seedColor: Color,
|
||||||
@@ -22,30 +22,117 @@ data class LegadoThemeColors(
|
|||||||
val composeEngine: String,
|
val composeEngine: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class LegadoColorScheme(
|
||||||
|
val primary: Color,
|
||||||
|
val onPrimary: Color,
|
||||||
|
val primaryContainer: Color,
|
||||||
|
val onPrimaryContainer: Color,
|
||||||
|
val inversePrimary: Color,
|
||||||
|
val secondary: Color,
|
||||||
|
val onSecondary: Color,
|
||||||
|
val secondaryContainer: Color,
|
||||||
|
val onSecondaryContainer: Color,
|
||||||
|
val tertiary: Color,
|
||||||
|
val onTertiary: Color,
|
||||||
|
val tertiaryContainer: Color,
|
||||||
|
val onTertiaryContainer: Color,
|
||||||
|
val background: Color,
|
||||||
|
val onBackground: Color,
|
||||||
|
val surface: Color,
|
||||||
|
val onSurface: Color,
|
||||||
|
val surfaceVariant: Color,
|
||||||
|
val onSurfaceVariant: Color,
|
||||||
|
val surfaceTint: Color,
|
||||||
|
val inverseSurface: Color,
|
||||||
|
val inverseOnSurface: Color,
|
||||||
|
val error: Color,
|
||||||
|
val onError: Color,
|
||||||
|
val errorContainer: Color,
|
||||||
|
val onErrorContainer: Color,
|
||||||
|
val outline: Color,
|
||||||
|
val outlineVariant: Color,
|
||||||
|
val scrim: Color,
|
||||||
|
val surfaceBright: Color,
|
||||||
|
val surfaceDim: Color,
|
||||||
|
val surfaceContainer: Color,
|
||||||
|
val surfaceContainerHigh: Color,
|
||||||
|
val surfaceContainerHighest: Color,
|
||||||
|
val surfaceContainerLow: Color,
|
||||||
|
val surfaceContainerLowest: Color,
|
||||||
|
val primaryFixed: Color,
|
||||||
|
val primaryFixedDim: Color,
|
||||||
|
val onPrimaryFixed: Color,
|
||||||
|
val onPrimaryFixedVariant: Color,
|
||||||
|
val secondaryFixed: Color,
|
||||||
|
val secondaryFixedDim: Color,
|
||||||
|
val onSecondaryFixed: Color,
|
||||||
|
val onSecondaryFixedVariant: Color,
|
||||||
|
val tertiaryFixed: Color,
|
||||||
|
val tertiaryFixedDim: Color,
|
||||||
|
val onTertiaryFixed: Color,
|
||||||
|
val onTertiaryFixedVariant: Color,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class LegadoTypography(
|
||||||
|
val headlineLarge: TextStyle,
|
||||||
|
val headlineLargeEmphasized: TextStyle,
|
||||||
|
val headlineMedium: TextStyle,
|
||||||
|
val headlineMediumEmphasized: TextStyle,
|
||||||
|
val headlineSmall: TextStyle,
|
||||||
|
val headlineSmallEmphasized: TextStyle,
|
||||||
|
|
||||||
|
val titleLarge: TextStyle,
|
||||||
|
val titleLargeEmphasized: TextStyle,
|
||||||
|
val titleMedium: TextStyle,
|
||||||
|
val titleMediumEmphasized: TextStyle,
|
||||||
|
val titleSmall: TextStyle,
|
||||||
|
val titleSmallEmphasized: TextStyle,
|
||||||
|
|
||||||
|
|
||||||
|
val bodyLarge: TextStyle,
|
||||||
|
val bodyLargeEmphasized: TextStyle,
|
||||||
|
val bodyMedium: TextStyle,
|
||||||
|
val bodyMediumEmphasized: TextStyle,
|
||||||
|
val bodySmall: TextStyle,
|
||||||
|
val bodySmallEmphasized: TextStyle,
|
||||||
|
|
||||||
|
val labelLarge: TextStyle,
|
||||||
|
val labelLargeEmphasized: TextStyle,
|
||||||
|
val labelMedium: TextStyle,
|
||||||
|
val labelMediumEmphasized: TextStyle,
|
||||||
|
val labelSmall: TextStyle,
|
||||||
|
val labelSmallEmphasized: TextStyle,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val LocalLegadoColorScheme = staticCompositionLocalOf<LegadoColorScheme> {
|
||||||
|
error("No ColorScheme provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
val LocalLegadoTypography = staticCompositionLocalOf<LegadoTypography> {
|
||||||
|
error("No Typography provided")
|
||||||
|
}
|
||||||
|
|
||||||
val LocalLegadoThemeColors = staticCompositionLocalOf {
|
val LocalLegadoThemeColors = staticCompositionLocalOf {
|
||||||
LegadoThemeColors(
|
LegadoThemeMode(
|
||||||
colorScheme = lightColorScheme(),
|
colorScheme = lightColorScheme(),
|
||||||
isDark = false,
|
isDark = false,
|
||||||
seedColor = Color.Unspecified,
|
seedColor = Color.Unspecified,
|
||||||
paletteStyle = PaletteStyle.TonalSpot,
|
paletteStyle = PaletteStyle.TonalSpot,
|
||||||
themeMode = ColorSchemeMode.System,
|
themeMode = ColorSchemeMode.System,
|
||||||
useDynamicColor = true,
|
useDynamicColor = true,
|
||||||
composeEngine = "m3"
|
composeEngine = "material"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val LocalLegadoTypography = staticCompositionLocalOf {
|
|
||||||
Typography()
|
|
||||||
}
|
|
||||||
|
|
||||||
val LocalHazeState = compositionLocalOf<HazeState?> { null }
|
val LocalHazeState = compositionLocalOf<HazeState?> { null }
|
||||||
|
|
||||||
object LegadoTheme {
|
object LegadoTheme {
|
||||||
|
|
||||||
val colorScheme: ColorScheme
|
val colorScheme: LegadoColorScheme
|
||||||
@Composable
|
@Composable
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
get() = LocalLegadoThemeColors.current.colorScheme
|
get() = LocalLegadoColorScheme.current
|
||||||
|
|
||||||
val isDark: Boolean
|
val isDark: Boolean
|
||||||
@Composable
|
@Composable
|
||||||
@@ -77,7 +164,7 @@ object LegadoTheme {
|
|||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
get() = LocalLegadoThemeColors.current.useDynamicColor
|
get() = LocalLegadoThemeColors.current.useDynamicColor
|
||||||
|
|
||||||
val typography: Typography
|
val typography: LegadoTypography
|
||||||
@Composable
|
@Composable
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
get() = LocalLegadoTypography.current
|
get() = LocalLegadoTypography.current
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ object ThemeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppThemeMode.CUSTOM -> {
|
AppThemeMode.CUSTOM -> {
|
||||||
CustomColorScheme(context.primaryColor, style)
|
val colorSpec = ThemeResolver.resolveColorSpecFromMaterialVersion(materialVersion)
|
||||||
|
CustomColorScheme(context.primaryColor, style, colorSpec)
|
||||||
.getColorScheme(darkTheme)
|
.getColorScheme(darkTheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,4 +67,12 @@ object ThemeResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resolveColorSpecFromMaterialVersion(value: String?): ThemeColorSpec {
|
||||||
|
return when (value) {
|
||||||
|
"material3Expressive" -> ThemeColorSpec.SPEC_2025
|
||||||
|
"material3" -> ThemeColorSpec.SPEC_2021
|
||||||
|
else -> ThemeColorSpec.SPEC_2021
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ExpandMore
|
import androidx.compose.material.icons.filled.ExpandMore
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -22,26 +20,26 @@ 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.widget.components.card.GlassCard
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CollapsibleHeader(
|
fun CollapsibleHeader(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
showIcon: Boolean = true,
|
||||||
isCollapsed: Boolean,
|
isCollapsed: Boolean,
|
||||||
onToggle: () -> Unit,
|
onToggle: () -> Unit,
|
||||||
title: String,
|
title: String,
|
||||||
subtitle: String? = null,
|
subtitle: String? = null,
|
||||||
titleContent: (@Composable ColumnScope.() -> Unit)? = null
|
titleContent: (@Composable ColumnScope.() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
Card(
|
GlassCard(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 16.dp, top = 4.dp, end = 16.dp),
|
.padding(start = 16.dp, top = 4.dp, end = 16.dp),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
colors = CardDefaults.cardColors(
|
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
|
||||||
),
|
|
||||||
onClick = onToggle
|
onClick = onToggle
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -59,7 +57,7 @@ fun CollapsibleHeader(
|
|||||||
text = title,
|
text = title,
|
||||||
style = LegadoTheme.typography.bodySmallEmphasized.copy(
|
style = LegadoTheme.typography.bodySmallEmphasized.copy(
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = LegadoTheme.colorScheme.primary
|
||||||
),
|
),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
@@ -68,7 +66,7 @@ fun CollapsibleHeader(
|
|||||||
AppText(
|
AppText(
|
||||||
text = it,
|
text = it,
|
||||||
style = LegadoTheme.typography.labelSmallEmphasized.copy(
|
style = LegadoTheme.typography.labelSmallEmphasized.copy(
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
),
|
),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
@@ -82,12 +80,14 @@ fun CollapsibleHeader(
|
|||||||
label = "arrowRotation"
|
label = "arrowRotation"
|
||||||
)
|
)
|
||||||
|
|
||||||
Icon(
|
if (showIcon) {
|
||||||
imageVector = Icons.Default.ExpandMore,
|
Icon(
|
||||||
contentDescription = if (isCollapsed) "展开" else "折叠",
|
imageVector = Icons.Default.ExpandMore,
|
||||||
modifier = Modifier.rotate(rotation),
|
contentDescription = if (isCollapsed) "展开" else "折叠",
|
||||||
tint = MaterialTheme.colorScheme.primary
|
modifier = Modifier.rotate(rotation),
|
||||||
)
|
tint = LegadoTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package io.legado.app.ui.widget.components
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
|
||||||
import io.legado.app.ui.theme.ThemeResolver
|
|
||||||
import androidx.compose.material3.LocalContentColor as MaterialLocalContentColor
|
|
||||||
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
|
|
||||||
import top.yukonga.miuix.kmp.theme.LocalContentColor as MiuixLocalContentColor
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AppIcon(
|
|
||||||
imageVector: ImageVector,
|
|
||||||
contentDescription: String?,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
tint: Color = currentContentColor(),
|
|
||||||
) {
|
|
||||||
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
|
||||||
|
|
||||||
if (isMiuix) {
|
|
||||||
MiuixIcon(
|
|
||||||
imageVector = imageVector,
|
|
||||||
contentDescription = contentDescription,
|
|
||||||
modifier = modifier,
|
|
||||||
tint = tint
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Icon(
|
|
||||||
imageVector = imageVector,
|
|
||||||
contentDescription = contentDescription,
|
|
||||||
modifier = modifier,
|
|
||||||
tint = tint
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun currentContentColor(): Color {
|
|
||||||
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
|
||||||
return if (isMiuix) {
|
|
||||||
MiuixLocalContentColor.current
|
|
||||||
} else {
|
|
||||||
MaterialLocalContentColor.current
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import androidx.compose.material3.BottomAppBarDefaults
|
|||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.NavigationBarItemDefaults
|
import androidx.compose.material3.NavigationBarItemDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -12,7 +13,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||||
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.widget.components.text.AppText
|
|
||||||
import top.yukonga.miuix.kmp.basic.NavigationBarDisplayMode
|
import top.yukonga.miuix.kmp.basic.NavigationBarDisplayMode
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
import top.yukonga.miuix.kmp.basic.NavigationBar as MiuixNavigationBar
|
import top.yukonga.miuix.kmp.basic.NavigationBar as MiuixNavigationBar
|
||||||
@@ -85,7 +85,7 @@ fun RowScope.AppNavigationBarItem(
|
|||||||
icon = m3Icon,
|
icon = m3Icon,
|
||||||
colors = NavigationBarItemDefaults.colors(indicatorColor = m3IndicatorColor),
|
colors = NavigationBarItemDefaults.colors(indicatorColor = m3IndicatorColor),
|
||||||
label = if (m3ShowLabel) {
|
label = if (m3ShowLabel) {
|
||||||
{ AppText(labelString) }
|
{ Text(labelString) }
|
||||||
} else null,
|
} else null,
|
||||||
alwaysShowLabel = m3AlwaysShowLabel
|
alwaysShowLabel = m3AlwaysShowLabel
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.text.input.InputTransformation
|
||||||
|
import androidx.compose.foundation.text.input.KeyboardActionHandler
|
||||||
|
import androidx.compose.foundation.text.input.OutputTransformation
|
||||||
|
import androidx.compose.foundation.text.input.TextFieldLineLimits
|
||||||
|
import androidx.compose.foundation.text.input.TextFieldState
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldColors
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.material3.TextFieldLabelPosition
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.graphics.takeOrElse
|
||||||
|
import androidx.compose.ui.text.TextLayoutResult
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.unit.Density
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
import top.yukonga.miuix.kmp.basic.TextField as MiuixTextField
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun AppTextField(
|
||||||
|
state: TextFieldState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
readOnly: Boolean = false,
|
||||||
|
// 1. 核心思想:保留 AppText 的统一样式与颜色降级参数
|
||||||
|
textStyle: TextStyle? = null,
|
||||||
|
textColor: Color = Color.Unspecified,
|
||||||
|
// 2. 将 Label 统一定义为 String 以兼容两套引擎
|
||||||
|
label: String? = null,
|
||||||
|
// 3. 保留 M3 TextField 的所有其他参数
|
||||||
|
labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Attached(),
|
||||||
|
placeholder: @Composable (() -> Unit)? = null,
|
||||||
|
leadingIcon: @Composable (() -> Unit)? = null,
|
||||||
|
trailingIcon: @Composable (() -> Unit)? = null,
|
||||||
|
prefix: @Composable (() -> Unit)? = null,
|
||||||
|
suffix: @Composable (() -> Unit)? = null,
|
||||||
|
supportingText: @Composable (() -> Unit)? = null,
|
||||||
|
isError: Boolean = false,
|
||||||
|
inputTransformation: InputTransformation? = null,
|
||||||
|
outputTransformation: OutputTransformation? = null,
|
||||||
|
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||||
|
onKeyboardAction: KeyboardActionHandler? = null,
|
||||||
|
lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
|
||||||
|
onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
|
||||||
|
scrollState: ScrollState = rememberScrollState(),
|
||||||
|
shape: Shape = TextFieldDefaults.shape,
|
||||||
|
colors: TextFieldColors = TextFieldDefaults.colors(),
|
||||||
|
contentPadding: PaddingValues? = null,
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
) {
|
||||||
|
val baseStyle = textStyle ?: LegadoTheme.typography.bodyMedium
|
||||||
|
val defaultTextColor = LegadoTheme.colorScheme.onSurface
|
||||||
|
val finalTextColor = textColor.takeOrElse {
|
||||||
|
baseStyle.color.takeOrElse { defaultTextColor }
|
||||||
|
}
|
||||||
|
val mergedTextStyle = baseStyle.merge(color = finalTextColor)
|
||||||
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
|
|
||||||
|
if (isMiuix) {
|
||||||
|
MiuixTextField(
|
||||||
|
state = state,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
readOnly = readOnly,
|
||||||
|
textStyle = MiuixTheme.textStyles.main,
|
||||||
|
label = label ?: "",
|
||||||
|
leadingIcon = leadingIcon,
|
||||||
|
trailingIcon = trailingIcon,
|
||||||
|
inputTransformation = inputTransformation,
|
||||||
|
outputTransformation = outputTransformation,
|
||||||
|
keyboardOptions = keyboardOptions,
|
||||||
|
onKeyboardAction = onKeyboardAction,
|
||||||
|
lineLimits = lineLimits,
|
||||||
|
onTextLayout = onTextLayout,
|
||||||
|
scrollState = scrollState,
|
||||||
|
interactionSource = interactionSource
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val resolvedContentPadding =
|
||||||
|
contentPadding ?: if (label == null || labelPosition is TextFieldLabelPosition.Above) {
|
||||||
|
TextFieldDefaults.contentPaddingWithoutLabel()
|
||||||
|
} else {
|
||||||
|
TextFieldDefaults.contentPaddingWithLabel()
|
||||||
|
}
|
||||||
|
TextField(
|
||||||
|
state = state,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
readOnly = readOnly,
|
||||||
|
textStyle = mergedTextStyle,
|
||||||
|
labelPosition = labelPosition,
|
||||||
|
label = label?.let { { AppText(it) } },
|
||||||
|
placeholder = placeholder,
|
||||||
|
leadingIcon = leadingIcon,
|
||||||
|
trailingIcon = trailingIcon,
|
||||||
|
prefix = prefix,
|
||||||
|
suffix = suffix,
|
||||||
|
supportingText = supportingText,
|
||||||
|
isError = isError,
|
||||||
|
inputTransformation = inputTransformation,
|
||||||
|
outputTransformation = outputTransformation,
|
||||||
|
keyboardOptions = keyboardOptions,
|
||||||
|
onKeyboardAction = onKeyboardAction,
|
||||||
|
lineLimits = lineLimits,
|
||||||
|
onTextLayout = onTextLayout,
|
||||||
|
scrollState = scrollState,
|
||||||
|
shape = shape,
|
||||||
|
colors = colors,
|
||||||
|
contentPadding = resolvedContentPadding,
|
||||||
|
interactionSource = interactionSource
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import androidx.compose.ui.unit.TextUnit
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
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.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||||
@@ -25,47 +23,36 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.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.button.SmallTextButton
|
import io.legado.app.ui.widget.components.button.SmallTextButton
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.settingItem.SettingItem
|
import io.legado.app.ui.widget.components.settingItem.SettingItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupManageBottomSheet(
|
fun GroupManageBottomSheet(
|
||||||
|
show: Boolean,
|
||||||
groups: List<String>,
|
groups: List<String>,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onUpdateGroup: (oldGroup: String, newGroup: String) -> Unit,
|
onUpdateGroup: (oldGroup: String, newGroup: String) -> Unit,
|
||||||
onDeleteGroup: (group: String) -> Unit
|
onDeleteGroup: (group: String) -> Unit
|
||||||
) {
|
) {
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
onDismissRequest = onDismissRequest
|
show = show,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
title = stringResource(R.string.group_manage),
|
||||||
) {
|
) {
|
||||||
Column(
|
LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
modifier = Modifier
|
items(groups, key = { it }) { group ->
|
||||||
.padding(horizontal = 16.dp)
|
GroupItem(
|
||||||
.padding(bottom = 16.dp),
|
group = group,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
onUpdateGroup = onUpdateGroup,
|
||||||
) {
|
onDeleteGroup = onDeleteGroup
|
||||||
AppText(
|
)
|
||||||
modifier = Modifier.padding(bottom = 16.dp),
|
|
||||||
text = stringResource(R.string.group_manage),
|
|
||||||
style = LegadoTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
|
||||||
items(groups, key = { it }) { group ->
|
|
||||||
GroupItem(
|
|
||||||
group = group,
|
|
||||||
onUpdateGroup = onUpdateGroup,
|
|
||||||
onDeleteGroup = onDeleteGroup
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,14 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.scaleIn
|
|
||||||
import androidx.compose.animation.scaleOut
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.input.TextFieldLineLimits
|
import androidx.compose.foundation.text.input.TextFieldLineLimits
|
||||||
import androidx.compose.foundation.text.input.clearText
|
|
||||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Clear
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
|
||||||
import androidx.compose.material.icons.filled.VerticalAlignTop
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -37,14 +19,15 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.ui.widget.components.button.SmallIconButton
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -53,10 +36,10 @@ fun SearchBarSection(
|
|||||||
onQueryChange: (String) -> Unit,
|
onQueryChange: (String) -> Unit,
|
||||||
placeholder: String = "搜索...",
|
placeholder: String = "搜索...",
|
||||||
leadingIcon: @Composable (() -> Unit)? = {
|
leadingIcon: @Composable (() -> Unit)? = {
|
||||||
SmallIconButton(
|
AppIcon(
|
||||||
icon = Icons.Default.Search,
|
imageVector = AppIcons.Search,
|
||||||
contentDescription = null,
|
contentDescription = null
|
||||||
onClick = {})
|
)
|
||||||
},
|
},
|
||||||
backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow,
|
backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||||
scrollState: LazyListState? = null,
|
scrollState: LazyListState? = null,
|
||||||
@@ -65,7 +48,6 @@ fun SearchBarSection(
|
|||||||
dropdownMenu: (@Composable (onDismiss: () -> Unit) -> Unit)? = null
|
dropdownMenu: (@Composable (onDismiss: () -> Unit) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val textFieldState = rememberTextFieldState(initialText = query)
|
val textFieldState = rememberTextFieldState(initialText = query)
|
||||||
|
|
||||||
LaunchedEffect(query) {
|
LaunchedEffect(query) {
|
||||||
@@ -91,79 +73,19 @@ fun SearchBarSection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface(
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
val searchTextField = @Composable {
|
||||||
.padding(start = 16.dp, end = 16.dp, bottom = 4.dp),
|
AppTextField(
|
||||||
shape = RoundedCornerShape(32.dp),
|
|
||||||
color = backgroundColor
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
state = textFieldState,
|
state = textFieldState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.heightIn(min = 48.dp)
|
.then(
|
||||||
.padding(horizontal = 4.dp),
|
if (isMiuix) Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
else Modifier
|
||||||
|
),
|
||||||
placeholder = { AppText(placeholder) },
|
placeholder = { AppText(placeholder) },
|
||||||
leadingIcon = leadingIcon,
|
leadingIcon = leadingIcon,
|
||||||
trailingIcon = {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(end = 4.dp)
|
|
||||||
.animateContentSize()
|
|
||||||
) {
|
|
||||||
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = textFieldState.text.isNotEmpty(),
|
|
||||||
enter = fadeIn() + scaleIn(),
|
|
||||||
exit = fadeOut() + scaleOut()
|
|
||||||
) {
|
|
||||||
SmallIconButton(
|
|
||||||
onClick = { textFieldState.clearText() },
|
|
||||||
icon = Icons.Default.Clear,
|
|
||||||
contentDescription = "清空输入"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = showScrollToTop,
|
|
||||||
enter = fadeIn() + scaleIn(),
|
|
||||||
exit = fadeOut() + scaleOut()
|
|
||||||
) {
|
|
||||||
SmallIconButton(
|
|
||||||
onClick = {
|
|
||||||
scope.launch {
|
|
||||||
scrollState?.animateScrollToItem(0)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon = Icons.Default.VerticalAlignTop,
|
|
||||||
contentDescription = "回到顶部"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trailingIcon != null) {
|
|
||||||
Box {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
if (dropdownMenu != null) showMenu = true
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
trailingIcon()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dropdownMenu != null) {
|
|
||||||
DropdownMenu(
|
|
||||||
expanded = showMenu,
|
|
||||||
onDismissRequest = { showMenu = false }
|
|
||||||
) {
|
|
||||||
dropdownMenu { showMenu = false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lineLimits = TextFieldLineLimits.SingleLine,
|
lineLimits = TextFieldLineLimits.SingleLine,
|
||||||
colors = TextFieldDefaults.colors(
|
colors = TextFieldDefaults.colors(
|
||||||
focusedContainerColor = Color.Transparent,
|
focusedContainerColor = Color.Transparent,
|
||||||
@@ -171,13 +93,21 @@ fun SearchBarSection(
|
|||||||
disabledContainerColor = Color.Transparent,
|
disabledContainerColor = Color.Transparent,
|
||||||
focusedIndicatorColor = Color.Transparent,
|
focusedIndicatorColor = Color.Transparent,
|
||||||
unfocusedIndicatorColor = Color.Transparent,
|
unfocusedIndicatorColor = Color.Transparent,
|
||||||
),
|
|
||||||
contentPadding = PaddingValues(
|
|
||||||
top = 4.dp,
|
|
||||||
bottom = 4.dp,
|
|
||||||
start = 12.dp,
|
|
||||||
end = 12.dp
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isMiuix) {
|
||||||
|
searchTextField()
|
||||||
|
} else {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 16.dp, end = 16.dp, bottom = 4.dp),
|
||||||
|
shape = RoundedCornerShape(32.dp),
|
||||||
|
color = backgroundColor
|
||||||
|
) {
|
||||||
|
searchTextField()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Remove
|
||||||
|
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.widget.components.button.SmallOutlinedIconButton
|
||||||
|
import io.legado.app.ui.widget.components.card.TextCard
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ValueStepper(
|
||||||
|
value: Float,
|
||||||
|
displayValue: Float,
|
||||||
|
valueRange: ClosedFloatingPointRange<Float>,
|
||||||
|
onValueChange: (Float) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
SmallOutlinedIconButton(
|
||||||
|
onClick = {
|
||||||
|
val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
|
||||||
|
onValueChange(newValue)
|
||||||
|
},
|
||||||
|
icon = Icons.Default.Remove
|
||||||
|
)
|
||||||
|
TextCard(
|
||||||
|
cornerRadius = 8.dp,
|
||||||
|
horizontalPadding = 8.dp,
|
||||||
|
verticalPadding = 4.dp,
|
||||||
|
text = displayValue.toInt().toString(),
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||||
|
contentColor = LegadoTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
SmallOutlinedIconButton(
|
||||||
|
onClick = {
|
||||||
|
val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
|
||||||
|
onValueChange(newValue)
|
||||||
|
},
|
||||||
|
icon = Icons.Default.Add
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
-25
@@ -7,13 +7,10 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -25,13 +22,15 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
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.widget.components.button.PrimaryButton
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.button.SecondaryButton
|
||||||
|
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
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BookmarkEditSheet(
|
fun BookmarkEditSheet(
|
||||||
|
show: Boolean,
|
||||||
bookmark: Bookmark,
|
bookmark: Bookmark,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onSave: (Bookmark) -> Unit,
|
onSave: (Bookmark) -> Unit,
|
||||||
@@ -42,21 +41,16 @@ fun BookmarkEditSheet(
|
|||||||
var bookText by remember { mutableStateOf(bookmark.bookText) }
|
var bookText by remember { mutableStateOf(bookmark.bookText) }
|
||||||
var content by remember { mutableStateOf(bookmark.content) }
|
var content by remember { mutableStateOf(bookmark.content) }
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
onDismissRequest = onDismiss
|
show = show,
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = bookmark.chapterName,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
) {
|
) {
|
||||||
AppText(
|
|
||||||
text = bookmark.chapterName,
|
|
||||||
style = LegadoTheme.typography.titleMedium,
|
|
||||||
modifier = Modifier.padding(bottom = 16.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = bookText,
|
value = bookText,
|
||||||
onValueChange = { bookText = it },
|
onValueChange = { bookText = it },
|
||||||
@@ -82,15 +76,13 @@ fun BookmarkEditSheet(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
OutlinedButton(
|
SecondaryButton(
|
||||||
onClick = { showDeleteConfirmDialog = true },
|
onClick = { showDeleteConfirmDialog = true },
|
||||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.error),
|
modifier = Modifier.weight(1f),
|
||||||
modifier = Modifier.weight(1f)
|
text = "删除"
|
||||||
) {
|
)
|
||||||
AppText("删除")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
PrimaryButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
val newBookmark = bookmark.apply {
|
val newBookmark = bookmark.apply {
|
||||||
this.bookText = bookText
|
this.bookText = bookText
|
||||||
@@ -98,10 +90,9 @@ fun BookmarkEditSheet(
|
|||||||
}
|
}
|
||||||
onSave(newBookmark)
|
onSave(newBookmark)
|
||||||
},
|
},
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f),
|
||||||
) {
|
text = "保存"
|
||||||
AppText("保存")
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -31,7 +30,7 @@ fun BookmarkItem(
|
|||||||
onLongClick: () -> Unit
|
onLongClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
val backgroundColor by animateColorAsState(
|
val backgroundColor by animateColorAsState(
|
||||||
targetValue = if (isDur) MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.3f)
|
targetValue = if (isDur) LegadoTheme.colorScheme.secondaryContainer.copy(alpha = 0.3f)
|
||||||
else Color.Transparent,
|
else Color.Transparent,
|
||||||
label = "BgColor"
|
label = "BgColor"
|
||||||
)
|
)
|
||||||
@@ -53,7 +52,7 @@ fun BookmarkItem(
|
|||||||
AppText(
|
AppText(
|
||||||
text = bookmark.chapterName,
|
text = bookmark.chapterName,
|
||||||
style = LegadoTheme.typography.titleSmallEmphasized,
|
style = LegadoTheme.typography.titleSmallEmphasized,
|
||||||
color = if (isDur) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary
|
color = if (isDur) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.secondary
|
||||||
)
|
)
|
||||||
|
|
||||||
if (bookmark.bookText.isNotEmpty()) {
|
if (bookmark.bookText.isNotEmpty()) {
|
||||||
@@ -63,7 +62,7 @@ fun BookmarkItem(
|
|||||||
style = LegadoTheme.typography.bodyMedium,
|
style = LegadoTheme.typography.bodyMedium,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = LegadoTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ fun BookmarkItem(
|
|||||||
AppText(
|
AppText(
|
||||||
text = bookmark.content,
|
text = bookmark.content,
|
||||||
style = LegadoTheme.typography.bodyMedium,
|
style = LegadoTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = LegadoTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package io.legado.app.ui.widget.components.button
|
||||||
|
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import top.yukonga.miuix.kmp.basic.Button as MiuixButton
|
||||||
|
import top.yukonga.miuix.kmp.basic.ButtonDefaults as MiuixButtonDefaults
|
||||||
|
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PrimaryButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
text: String
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
MiuixButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
colors = MiuixButtonDefaults.buttonColorsPrimary()
|
||||||
|
) {
|
||||||
|
MiuixText(
|
||||||
|
text = text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SecondaryButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
text: String
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
MiuixButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
colors = MiuixButtonDefaults.buttonColors()
|
||||||
|
) {
|
||||||
|
MiuixText(
|
||||||
|
text = text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package io.legado.app.ui.widget.components.button
|
||||||
|
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonColors
|
||||||
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||||
|
import top.yukonga.miuix.kmp.basic.IconButtonDefaults as MiuixIconButtonDefaults
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AppIconButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
//M3
|
||||||
|
colors: IconButtonColors = IconButtonDefaults.iconButtonColors(),
|
||||||
|
shape: Shape = IconButtonDefaults.standardShape,
|
||||||
|
// MIUIX
|
||||||
|
holdDownState: Boolean = false,
|
||||||
|
miuixBackgroundColor: Color = Color.Unspecified,
|
||||||
|
miuixCornerRadius: Dp? = null,
|
||||||
|
miuixMinHeight: Dp? = null,
|
||||||
|
miuixMinWidth: Dp? = null,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
|
|
||||||
|
if (isMiuix) {
|
||||||
|
MiuixIconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
holdDownState = holdDownState,
|
||||||
|
backgroundColor = miuixBackgroundColor,
|
||||||
|
cornerRadius = MiuixIconButtonDefaults.CornerRadius,
|
||||||
|
minHeight = MiuixIconButtonDefaults.MinHeight,
|
||||||
|
minWidth = MiuixIconButtonDefaults.MinWidth,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
IconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
colors = colors,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
shape = shape,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -7,21 +7,21 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import top.yukonga.miuix.kmp.basic.Icon
|
import top.yukonga.miuix.kmp.basic.Icon
|
||||||
import top.yukonga.miuix.kmp.basic.IconButton
|
|
||||||
import top.yukonga.miuix.kmp.icon.MiuixIcons
|
import top.yukonga.miuix.kmp.icon.MiuixIcons
|
||||||
import top.yukonga.miuix.kmp.icon.extended.Back
|
import top.yukonga.miuix.kmp.icon.extended.Back
|
||||||
import top.yukonga.miuix.kmp.theme.LocalContentColor
|
import top.yukonga.miuix.kmp.theme.LocalContentColor
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MiuixButton(
|
fun MiuixIconButton(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
imageVector: ImageVector = MiuixIcons.Regular.Back,
|
imageVector: ImageVector = MiuixIcons.Regular.Back,
|
||||||
tint: Color = LocalContentColor.current,
|
tint: Color = LocalContentColor.current,
|
||||||
contentDescription: String? = stringResource(id = R.string.back),
|
contentDescription: String? = stringResource(id = R.string.back),
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
MiuixIconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
@@ -44,7 +44,7 @@ fun MiuixToggleButton(
|
|||||||
uncheckedTint: Color = LocalContentColor.current,
|
uncheckedTint: Color = LocalContentColor.current,
|
||||||
contentDescription: String? = null,
|
contentDescription: String? = null,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
MiuixIconButton(
|
||||||
onClick = { onCheckedChange(!checked) },
|
onClick = { onCheckedChange(!checked) },
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package io.legado.app.ui.widget.components.button
|
package io.legado.app.ui.widget.components.button
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -80,8 +82,24 @@ fun SmallIconButton(
|
|||||||
fun SmallOutlinedIconButton(
|
fun SmallOutlinedIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
||||||
|
if (isMiuix) {
|
||||||
|
MiuixIconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier.size(32.dp),
|
||||||
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
|
) {
|
||||||
|
MiuixIcon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||||
OutlinedIconButton(
|
OutlinedIconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -102,6 +120,7 @@ fun SmallOutlinedIconButton(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@@ -109,27 +128,42 @@ fun SmallOutlinedIconButton(
|
|||||||
fun SmallTonalIconButton(
|
fun SmallTonalIconButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
||||||
FilledTonalIconButton(
|
|
||||||
|
if (isMiuix) {
|
||||||
|
MiuixIconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = Modifier.size(
|
modifier = modifier.size(32.dp),
|
||||||
IconButtonDefaults.extraSmallContainerSize(
|
backgroundColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
IconButtonDefaults.IconButtonWidthOption.Uniform
|
|
||||||
)
|
|
||||||
),
|
|
||||||
shapes = IconButtonDefaults.shapes(),
|
|
||||||
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(
|
modifier = Modifier.size(18.dp)
|
||||||
IconButtonDefaults.extraSmallIconSize
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||||
|
FilledTonalIconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier.size(
|
||||||
|
IconButtonDefaults.extraSmallContainerSize(
|
||||||
|
IconButtonDefaults.IconButtonWidthOption.Uniform
|
||||||
|
)
|
||||||
|
),
|
||||||
|
shapes = IconButtonDefaults.shapes(),
|
||||||
|
colors = IconButtonDefaults.filledTonalIconButtonColors()
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.size(IconButtonDefaults.extraSmallIconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,35 +176,67 @@ fun SmallOutlinedIconToggleButton(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null
|
contentDescription: String? = null
|
||||||
) {
|
) {
|
||||||
val defaultShape = IconButtonDefaults.extraSmallRoundShape
|
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
||||||
val pressedShape = IconButtonDefaults.extraSmallPressedShape
|
|
||||||
val checkedShape = IconButtonDefaults.extraSmallSelectedRoundShape
|
|
||||||
|
|
||||||
val toggleShapes = remember(defaultShape, checkedShape) {
|
if (isMiuix) {
|
||||||
IconToggleButtonShapes(
|
|
||||||
shape = defaultShape,
|
val containerColor by animateColorAsState(
|
||||||
pressedShape = pressedShape,
|
targetValue = if (checked) LegadoTheme.colorScheme.primaryContainer else LegadoTheme.colorScheme.surfaceContainer,
|
||||||
checkedShape = checkedShape
|
animationSpec = tween(150),
|
||||||
|
label = "MiuixToggleContainerColor"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
val iconTint by animateColorAsState(
|
||||||
OutlinedIconToggleButton(
|
targetValue = if (checked) LegadoTheme.colorScheme.onPrimaryContainer else LegadoTheme.colorScheme.onSurfaceVariant,
|
||||||
checked = checked,
|
animationSpec = tween(150),
|
||||||
onCheckedChange = onCheckedChange,
|
label = "MiuixToggleIconTint"
|
||||||
modifier = modifier.size(
|
)
|
||||||
IconButtonDefaults.extraSmallContainerSize(
|
|
||||||
IconButtonDefaults.IconButtonWidthOption.Uniform
|
MiuixIconButton(
|
||||||
)
|
onClick = { onCheckedChange(!checked) },
|
||||||
),
|
modifier = modifier
|
||||||
shapes = toggleShapes
|
.size(32.dp),
|
||||||
|
backgroundColor = containerColor
|
||||||
) {
|
) {
|
||||||
Icon(
|
MiuixIcon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = Modifier.size(IconButtonDefaults.extraSmallIconSize),
|
tint = iconTint,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
val defaultShape = IconButtonDefaults.extraSmallRoundShape
|
||||||
|
val pressedShape = IconButtonDefaults.extraSmallPressedShape
|
||||||
|
val checkedShape = IconButtonDefaults.extraSmallSelectedRoundShape
|
||||||
|
|
||||||
|
val toggleShapes = remember(defaultShape, checkedShape) {
|
||||||
|
IconToggleButtonShapes(
|
||||||
|
shape = defaultShape,
|
||||||
|
pressedShape = pressedShape,
|
||||||
|
checkedShape = checkedShape
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||||
|
OutlinedIconToggleButton(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
|
modifier = modifier.size(
|
||||||
|
IconButtonDefaults.extraSmallContainerSize(
|
||||||
|
IconButtonDefaults.IconButtonWidthOption.Uniform
|
||||||
|
)
|
||||||
|
),
|
||||||
|
shapes = toggleShapes
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.size(IconButtonDefaults.extraSmallIconSize),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
import top.yukonga.miuix.kmp.basic.Button as MiuixButton
|
||||||
|
import top.yukonga.miuix.kmp.basic.ButtonDefaults as MiuixButtonDefaults
|
||||||
|
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SmallTextButton(
|
fun SmallTextButton(
|
||||||
@@ -21,21 +26,33 @@ fun SmallTextButton(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
TextButton(
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
onClick = onClick,
|
MiuixButton(
|
||||||
modifier = modifier,
|
onClick = onClick,
|
||||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
modifier = modifier,
|
||||||
shape = MaterialTheme.shapes.small
|
colors = MiuixButtonDefaults.buttonColors()
|
||||||
) {
|
) {
|
||||||
Icon(
|
MiuixText(
|
||||||
imageVector = icon,
|
text = text
|
||||||
contentDescription = null,
|
)
|
||||||
modifier = Modifier.size(16.dp)
|
}
|
||||||
)
|
} else {
|
||||||
Spacer(Modifier.width(4.dp))
|
TextButton(
|
||||||
AppText(
|
onClick = onClick,
|
||||||
text = text,
|
modifier = modifier,
|
||||||
style = LegadoTheme.typography.labelMedium
|
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
||||||
)
|
shape = MaterialTheme.shapes.small
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(4.dp))
|
||||||
|
AppText(
|
||||||
|
text = text,
|
||||||
|
style = LegadoTheme.typography.labelMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||||
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.widget.components.icon.AppIcons
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -107,12 +108,12 @@ fun TopBarButton(
|
|||||||
fun TopbarNavigationButton(
|
fun TopbarNavigationButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
imageVector: ImageVector = AppIcons.Back,
|
||||||
contentDescription: String? = "返回",
|
contentDescription: String? = "返回",
|
||||||
style: TopBarButtonVariant = TopBarButtonVariant.Filled
|
style: TopBarButtonVariant = TopBarButtonVariant.Filled
|
||||||
) {
|
) {
|
||||||
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
||||||
MiuixButton(
|
MiuixIconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
imageVector = imageVector,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
@@ -138,7 +139,7 @@ fun TopBarActionButton(
|
|||||||
) {
|
) {
|
||||||
val enableProgressive = ThemeConfig.enableProgressiveBlur
|
val enableProgressive = ThemeConfig.enableProgressiveBlur
|
||||||
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
||||||
MiuixButton(
|
MiuixIconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
imageVector = imageVector,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ fun SelectionItemCard(
|
|||||||
GlassCard(
|
GlassCard(
|
||||||
onClick = onToggleSelection,
|
onClick = onToggleSelection,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
containerColor = animatedContainerColor,
|
containerColor = animatedContainerColor,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -17,11 +16,8 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
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.LegadoTheme.composeEngine
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
import io.legado.app.ui.theme.ThemeResolver
|
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
||||||
import io.legado.app.ui.widget.components.AnimatedTextLine
|
|
||||||
import io.legado.app.ui.widget.components.AppIcon
|
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@@ -40,17 +36,8 @@ fun TextCard(
|
|||||||
spacing: Dp = 4.dp,
|
spacing: Dp = 4.dp,
|
||||||
textStyle: TextStyle = LegadoTheme.typography.labelSmallEmphasized
|
textStyle: TextStyle = LegadoTheme.typography.labelSmallEmphasized
|
||||||
) {
|
) {
|
||||||
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
|
val defaultBackground = LegadoTheme.colorScheme.surfaceContainer
|
||||||
|
val defaultContent = LegadoTheme.colorScheme.onSurface
|
||||||
val defaultBackground = if (isMiuix)
|
|
||||||
MiuixTheme.colorScheme.surfaceContainer
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.primaryContainer
|
|
||||||
|
|
||||||
val defaultContent = if (isMiuix)
|
|
||||||
MiuixTheme.colorScheme.onSurface
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.primary
|
|
||||||
|
|
||||||
val finalBackgroundColor = backgroundColor ?: defaultBackground
|
val finalBackgroundColor = backgroundColor ?: defaultBackground
|
||||||
val finalContentColor = contentColor ?: defaultContent
|
val finalContentColor = contentColor ?: defaultContent
|
||||||
|
|||||||
@@ -7,17 +7,13 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Update
|
import androidx.compose.material.icons.filled.Update
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
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.LegadoTheme.composeEngine
|
|
||||||
import io.legado.app.ui.theme.ThemeResolver
|
|
||||||
import io.legado.app.ui.widget.components.card.TextCard
|
import io.legado.app.ui.widget.components.card.TextCard
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BookshelfCover(
|
fun BookshelfCover(
|
||||||
@@ -31,16 +27,6 @@ fun BookshelfCover(
|
|||||||
sourceOrigin: String? = null,
|
sourceOrigin: String? = null,
|
||||||
onLoadFinish: (() -> Unit)? = null
|
onLoadFinish: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val backgroundColor = if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
|
||||||
MiuixTheme.colorScheme.primaryContainer.copy(alpha = 0.2f)
|
|
||||||
} else {
|
|
||||||
MaterialTheme.colorScheme.secondaryContainer
|
|
||||||
}
|
|
||||||
val contentColor = if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
|
||||||
MiuixTheme.colorScheme.primary
|
|
||||||
} else {
|
|
||||||
MaterialTheme.colorScheme.onSecondaryContainer
|
|
||||||
}
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
BookCover(
|
BookCover(
|
||||||
name = name,
|
name = name,
|
||||||
@@ -58,8 +44,6 @@ fun BookshelfCover(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd)
|
||||||
.padding(2.dp),
|
.padding(2.dp),
|
||||||
backgroundColor = backgroundColor,
|
|
||||||
contentColor = contentColor,
|
|
||||||
cornerRadius = 4.dp,
|
cornerRadius = 4.dp,
|
||||||
horizontalPadding = 4.dp,
|
horizontalPadding = 4.dp,
|
||||||
verticalPadding = 0.dp
|
verticalPadding = 0.dp
|
||||||
|
|||||||
@@ -41,12 +41,13 @@ import com.github.skydoves.colorpicker.compose.HsvColorPicker
|
|||||||
import com.github.skydoves.colorpicker.compose.rememberColorPickerController
|
import com.github.skydoves.colorpicker.compose.rememberColorPickerController
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ColorPickerSheet(
|
fun ColorPickerSheet(
|
||||||
|
show: Boolean,
|
||||||
initialColor: Int,
|
initialColor: Int,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onColorSelected: (Int) -> Unit
|
onColorSelected: (Int) -> Unit
|
||||||
@@ -80,7 +81,7 @@ fun ColorPickerSheet(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
|
AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -14,26 +13,39 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PillDivider(
|
fun PillDivider(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
thickness: Dp = 2.dp,
|
thickness: Dp = 2.dp,
|
||||||
widthFraction: Float = 0.2f,
|
widthFraction: Float = 0.2f,
|
||||||
color: Color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f)
|
color: Color = LegadoTheme.colorScheme.outlineVariant.copy(alpha = 0.6f)
|
||||||
) {
|
) {
|
||||||
Box(
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
modifier = modifier
|
if (isMiuix) {
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 4.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(widthFraction)
|
.fillMaxWidth()
|
||||||
.height(thickness)
|
.height(4.dp)
|
||||||
.clip(CircleShape)
|
.background(MiuixTheme.colorScheme.secondaryContainer)
|
||||||
.background(color)
|
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(widthFraction)
|
||||||
|
.height(thickness)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(color)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -24,8 +23,8 @@ fun PillHeaderDivider(
|
|||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
dotSize: Dp = 2.dp,
|
dotSize: Dp = 2.dp,
|
||||||
dotColor: Color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.8f),
|
dotColor: Color = LegadoTheme.colorScheme.outlineVariant.copy(alpha = 0.8f),
|
||||||
titleColor: Color = MaterialTheme.colorScheme.primary
|
titleColor: Color = LegadoTheme.colorScheme.primary
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|||||||
+11
-16
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
||||||
@@ -17,7 +16,6 @@ import androidx.compose.material.icons.filled.CloudUpload
|
|||||||
import androidx.compose.material.icons.filled.EditNote
|
import androidx.compose.material.icons.filled.EditNote
|
||||||
import androidx.compose.material.icons.filled.FolderOpen
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -28,7 +26,8 @@ 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.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
|
||||||
enum class FilePickerSheetMode {
|
enum class FilePickerSheetMode {
|
||||||
@@ -38,6 +37,7 @@ enum class FilePickerSheetMode {
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun FilePickerSheet(
|
fun FilePickerSheet(
|
||||||
|
show: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
title: String = stringResource(R.string.select_operation),
|
title: String = stringResource(R.string.select_operation),
|
||||||
onSelectSysDir: (() -> Unit)? = null,
|
onSelectSysDir: (() -> Unit)? = null,
|
||||||
@@ -47,22 +47,17 @@ fun FilePickerSheet(
|
|||||||
onUpload: (() -> Unit)? = null,
|
onUpload: (() -> Unit)? = null,
|
||||||
allowExtensions: Array<String>? = null,
|
allowExtensions: Array<String>? = null,
|
||||||
) {
|
) {
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
|
title = title,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
containerColor = { it.surface }
|
containerColor = LegadoTheme.colorScheme.surface
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth(),
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
AppText(
|
|
||||||
text = title,
|
|
||||||
style = LegadoTheme.typography.titleMedium,
|
|
||||||
modifier = Modifier.padding(bottom = 20.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
@@ -127,7 +122,7 @@ private fun RowScope.FilePickerOptionCard(
|
|||||||
.weight(1f)
|
.weight(1f)
|
||||||
.height(100.dp),
|
.height(100.dp),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
color = MaterialTheme.colorScheme.surfaceContainerLow,
|
color = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||||
tonalElevation = 2.dp
|
tonalElevation = 2.dp
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -135,11 +130,11 @@ private fun RowScope.FilePickerOptionCard(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
AppIcon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(32.dp),
|
modifier = Modifier.size(32.dp),
|
||||||
tint = MaterialTheme.colorScheme.primary
|
tint = LegadoTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
AppText(
|
AppText(
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package io.legado.app.ui.widget.components.icon
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.paint
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.geometry.isUnspecified
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.graphics.takeOrElse
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.role
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. 基础 AppIcon 实现
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun AppIcon(
|
||||||
|
painter: Painter,
|
||||||
|
contentDescription: String?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
tint: Color = Color.Unspecified,
|
||||||
|
) {
|
||||||
|
val defaultTintColor = LegadoTheme.colorScheme.onSurface
|
||||||
|
val finalTint = tint.takeOrElse { defaultTintColor }
|
||||||
|
|
||||||
|
val colorFilter = remember(finalTint) {
|
||||||
|
if (finalTint == Color.Unspecified) null else ColorFilter.tint(finalTint)
|
||||||
|
}
|
||||||
|
|
||||||
|
val semantics = if (contentDescription != null) {
|
||||||
|
Modifier.semantics {
|
||||||
|
this.contentDescription = contentDescription
|
||||||
|
this.role = Role.Image
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier
|
||||||
|
.defaultIconSize(painter)
|
||||||
|
.paint(painter, colorFilter = colorFilter, contentScale = ContentScale.Fit)
|
||||||
|
.then(semantics)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AppIcon(
|
||||||
|
imageVector: ImageVector?,
|
||||||
|
contentDescription: String?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
tint: Color = Color.Unspecified,
|
||||||
|
) {
|
||||||
|
if (imageVector == null) return
|
||||||
|
|
||||||
|
AppIcon(
|
||||||
|
painter = rememberVectorPainter(imageVector),
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = modifier,
|
||||||
|
tint = tint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3. 辅助扩展函数:处理图标的默认尺寸
|
||||||
|
* M3 和 MIUIX 内部都有 defaultSizeFor,如果 painter 没有固有尺寸,默认设为 24.dp
|
||||||
|
*/
|
||||||
|
private fun Modifier.defaultIconSize(painter: Painter): Modifier {
|
||||||
|
return this.then(
|
||||||
|
if (painter.intrinsicSize == Size.Unspecified || painter.intrinsicSize.isUnspecified) {
|
||||||
|
Modifier.size(24.dp)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package io.legado.app.ui.widget.components.icon
|
||||||
|
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Clear
|
||||||
|
import androidx.compose.material.icons.filled.Replay
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import top.yukonga.miuix.kmp.icon.MiuixIcons
|
||||||
|
import top.yukonga.miuix.kmp.icon.basic.Search
|
||||||
|
import top.yukonga.miuix.kmp.icon.extended.Back
|
||||||
|
import top.yukonga.miuix.kmp.icon.extended.Close
|
||||||
|
import top.yukonga.miuix.kmp.icon.extended.Refresh
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用全局图标映射层
|
||||||
|
*/
|
||||||
|
object AppIcons {
|
||||||
|
|
||||||
|
private val isMiuix: Boolean
|
||||||
|
@Composable
|
||||||
|
get() = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
|
|
||||||
|
val Search: ImageVector
|
||||||
|
@Composable
|
||||||
|
get() = if (isMiuix) {
|
||||||
|
MiuixIcons.Basic.Search
|
||||||
|
} else {
|
||||||
|
Icons.Default.Search
|
||||||
|
}
|
||||||
|
|
||||||
|
val Close: ImageVector
|
||||||
|
@Composable
|
||||||
|
get() = if (isMiuix) {
|
||||||
|
MiuixIcons.Close
|
||||||
|
} else {
|
||||||
|
Icons.Default.Clear
|
||||||
|
}
|
||||||
|
|
||||||
|
val Back: ImageVector
|
||||||
|
@Composable
|
||||||
|
get() = if (isMiuix) {
|
||||||
|
MiuixIcons.Back
|
||||||
|
} else {
|
||||||
|
Icons.AutoMirrored.Filled.ArrowBack
|
||||||
|
}
|
||||||
|
|
||||||
|
val Replay: ImageVector
|
||||||
|
@Composable
|
||||||
|
get() = if (isMiuix) MiuixIcons.Refresh ?: Icons.Default.Replay else Icons.Default.Replay
|
||||||
|
}
|
||||||
+73
-108
@@ -1,10 +1,6 @@
|
|||||||
package io.legado.app.ui.widget.components.importComponents
|
package io.legado.app.ui.widget.components.importComponents
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import androidx.compose.animation.animateColorAsState
|
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@@ -26,16 +22,10 @@ import androidx.compose.material.icons.filled.SelectAll
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AssistChip
|
import androidx.compose.material3.AssistChip
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.ListItem
|
|
||||||
import androidx.compose.material3.ListItemDefaults
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
@@ -44,7 +34,6 @@ import androidx.compose.material3.SnackbarHost
|
|||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -59,9 +48,11 @@ 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.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.AnimatedText
|
|
||||||
import io.legado.app.ui.widget.components.AppScaffold
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
|
import io.legado.app.ui.widget.components.button.SmallIconButton
|
||||||
|
import io.legado.app.ui.widget.components.card.SelectionItemCard
|
||||||
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
|
import io.legado.app.ui.widget.components.text.AnimatedText
|
||||||
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
|
||||||
|
|
||||||
@@ -116,31 +107,42 @@ fun SourceInputDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: 动画
|
||||||
@SuppressLint("ConfigurationScreenWidthHeight")
|
@SuppressLint("ConfigurationScreenWidthHeight")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> BatchImportDialog(
|
fun <T> BatchImportDialog(
|
||||||
title: String,
|
title: String,
|
||||||
importState: BaseImportUiState.Success<T>,
|
importState: BaseImportUiState<T>,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onConfirm: (List<T>) -> Unit, // 返回选中的原始数据列表
|
onConfirm: (List<T>) -> Unit,
|
||||||
onToggleItem: (index: Int) -> Unit,
|
onToggleItem: (index: Int) -> Unit,
|
||||||
onToggleAll: (isSelected: Boolean) -> Unit,
|
onToggleAll: (isSelected: Boolean) -> Unit,
|
||||||
onItemInfoClick: (index: Int) -> Unit = {},
|
onItemInfoClick: (index: Int) -> Unit = {},
|
||||||
// 插槽:允许调用方自定义顶部菜单
|
|
||||||
topBarActions: @Composable RowScope.() -> Unit = {},
|
topBarActions: @Composable RowScope.() -> Unit = {},
|
||||||
// 插槽:自定义每一行的显示内容
|
itemTitle: (data: T) -> String,
|
||||||
itemContent: @Composable (data: T, status: ImportStatus) -> Unit
|
itemSubtitle: (data: T) -> String? = { null }
|
||||||
) {
|
) {
|
||||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val show = importState is BaseImportUiState.Success<T>
|
||||||
|
|
||||||
|
var cachedState by remember { mutableStateOf<BaseImportUiState.Success<T>?>(null) }
|
||||||
|
if (importState is BaseImportUiState.Success<T>) {
|
||||||
|
cachedState = importState
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!show && cachedState == null) return
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
val selectedCount = importState.items.count { it.isSelected }
|
|
||||||
val totalCount = importState.items.size
|
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
val currentState = cachedState!!
|
||||||
|
val selectedCount = currentState.items.count { it.isSelected }
|
||||||
|
val totalCount = currentState.items.size
|
||||||
|
|
||||||
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
containerColor = { it.surfaceContainer }
|
containerColor = LegadoTheme.colorScheme.surfaceContainer
|
||||||
) {
|
) {
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -171,44 +173,34 @@ fun <T> BatchImportDialog(
|
|||||||
ImportBottomBar(
|
ImportBottomBar(
|
||||||
selectedCount = selectedCount,
|
selectedCount = selectedCount,
|
||||||
totalCount = totalCount,
|
totalCount = totalCount,
|
||||||
onToggleSelectAll = { isAll -> onToggleAll(isAll) },
|
onToggleSelectAll = onToggleAll,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
scope.launch { sheetState.hide() }.invokeOnCompletion {
|
val selectedData =
|
||||||
if (!sheetState.isVisible) {
|
currentState.items.filter { it.isSelected }.map { it.data }
|
||||||
val selectedData =
|
onConfirm(selectedData)
|
||||||
importState.items.filter { it.isSelected }.map { it.data }
|
|
||||||
onConfirm(selectedData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onCancel = {
|
onCancel = onDismissRequest
|
||||||
scope.launch { sheetState.hide() }.invokeOnCompletion {
|
|
||||||
if (!sheetState.isVisible) {
|
|
||||||
onDismissRequest()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier.padding(padding),
|
||||||
.padding(padding),
|
|
||||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
|
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
itemsIndexed(importState.items, key = { _, item -> item.data.hashCode() }) { index, itemWrapper ->
|
itemsIndexed(
|
||||||
|
currentState.items,
|
||||||
|
key = { _, item -> item.data.hashCode() }) { index, itemWrapper ->
|
||||||
ImportItemRow(
|
ImportItemRow(
|
||||||
|
title = itemTitle(itemWrapper.data),
|
||||||
|
subtitle = itemSubtitle(itemWrapper.data),
|
||||||
isSelected = itemWrapper.isSelected,
|
isSelected = itemWrapper.isSelected,
|
||||||
status = itemWrapper.status,
|
status = itemWrapper.status,
|
||||||
onClick = { onToggleItem(index) },
|
onClick = { onToggleItem(index) },
|
||||||
onInfoClick = {
|
onInfoClick = {
|
||||||
scope.launch {
|
scope.launch { snackbarHostState.showSnackbar("其实还没写桀桀桀") }
|
||||||
snackbarHostState.showSnackbar("其实还没写桀桀桀")
|
|
||||||
}
|
|
||||||
onItemInfoClick(index)
|
onItemInfoClick(index)
|
||||||
},
|
}
|
||||||
content = { itemContent(itemWrapper.data, itemWrapper.status) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,74 +208,47 @@ fun <T> BatchImportDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ImportItemRow(
|
fun ImportItemRow(
|
||||||
|
title: String,
|
||||||
|
subtitle: String? = null,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
status: ImportStatus,
|
status: ImportStatus,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onInfoClick: () -> Unit,
|
onInfoClick: () -> Unit
|
||||||
content: @Composable () -> Unit
|
|
||||||
) {
|
) {
|
||||||
val containerColor by animateColorAsState(
|
SelectionItemCard(
|
||||||
targetValue = if (isSelected)
|
title = title,
|
||||||
MaterialTheme.colorScheme.secondaryContainer
|
subtitle = subtitle,
|
||||||
else
|
isSelected = isSelected,
|
||||||
MaterialTheme.colorScheme.surface,
|
inSelectionMode = true,
|
||||||
animationSpec = tween(
|
onToggleSelection = onClick,
|
||||||
durationMillis = 200,
|
modifier = Modifier.padding(vertical = 4.dp),
|
||||||
easing = FastOutSlowInEasing
|
trailingAction = {
|
||||||
),
|
AppText(
|
||||||
label = "CardColor"
|
text = when (status) {
|
||||||
)
|
ImportStatus.New -> "新增"
|
||||||
|
ImportStatus.Update -> "更新"
|
||||||
Card(
|
ImportStatus.Existing -> "已有"
|
||||||
onClick = onClick,
|
ImportStatus.Error -> "错误"
|
||||||
modifier = Modifier.fillMaxWidth(),
|
},
|
||||||
shape = MaterialTheme.shapes.medium,
|
style = LegadoTheme.typography.labelMedium,
|
||||||
colors = CardDefaults.cardColors(
|
color = when (status) {
|
||||||
containerColor = containerColor
|
ImportStatus.New -> MaterialTheme.colorScheme.primary
|
||||||
)
|
ImportStatus.Update -> MaterialTheme.colorScheme.secondary
|
||||||
) {
|
ImportStatus.Error -> MaterialTheme.colorScheme.error
|
||||||
ListItem(
|
else -> MaterialTheme.colorScheme.outline
|
||||||
modifier = Modifier.animateContentSize(),
|
},
|
||||||
headlineContent = { content() },
|
modifier = Modifier.padding(end = 4.dp)
|
||||||
leadingContent = {
|
|
||||||
Checkbox(
|
|
||||||
checked = isSelected,
|
|
||||||
onCheckedChange = null
|
|
||||||
)
|
|
||||||
},
|
|
||||||
supportingContent = {
|
|
||||||
AppText(
|
|
||||||
text = when (status) {
|
|
||||||
ImportStatus.New -> "新增"
|
|
||||||
ImportStatus.Update -> "更新"
|
|
||||||
ImportStatus.Existing -> "已有"
|
|
||||||
ImportStatus.Error -> "错误"
|
|
||||||
},
|
|
||||||
style = LegadoTheme.typography.labelMedium,
|
|
||||||
color = when (status) {
|
|
||||||
ImportStatus.New -> MaterialTheme.colorScheme.primary
|
|
||||||
ImportStatus.Update -> MaterialTheme.colorScheme.secondary
|
|
||||||
ImportStatus.Error -> MaterialTheme.colorScheme.error
|
|
||||||
else -> MaterialTheme.colorScheme.outline
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingContent = {
|
|
||||||
IconButton(onClick = onInfoClick) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Info,
|
|
||||||
contentDescription = "详情"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colors = ListItemDefaults.colors(
|
|
||||||
containerColor = Color.Transparent
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
SmallIconButton(
|
||||||
|
onClick = onInfoClick,
|
||||||
|
icon = Icons.Default.Info,
|
||||||
|
contentDescription = "详情"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
|||||||
+2
-1
@@ -46,6 +46,7 @@ import androidx.compose.ui.util.fastFirstOrNull
|
|||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
import androidx.compose.ui.util.fastLastOrNull
|
import androidx.compose.ui.util.fastLastOrNull
|
||||||
import androidx.compose.ui.util.fastMaxBy
|
import androidx.compose.ui.util.fastMaxBy
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.widget.components.lazylist.Scroller.STICKY_HEADER_KEY_PREFIX
|
import io.legado.app.ui.widget.components.lazylist.Scroller.STICKY_HEADER_KEY_PREFIX
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@@ -67,7 +68,7 @@ fun VerticalFastScroller(
|
|||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
thumbAllowed: () -> Boolean = { true },
|
thumbAllowed: () -> Boolean = { true },
|
||||||
thumbColor: Color = MaterialTheme.colorScheme.primary,
|
thumbColor: Color = LegadoTheme.colorScheme.primary,
|
||||||
topContentPadding: Dp = Dp.Hairline,
|
topContentPadding: Dp = Dp.Hairline,
|
||||||
bottomContentPadding: Dp = Dp.Hairline,
|
bottomContentPadding: Dp = Dp.Hairline,
|
||||||
endContentPadding: Dp = Dp.Hairline,
|
endContentPadding: Dp = Dp.Hairline,
|
||||||
|
|||||||
+42
-18
@@ -3,6 +3,8 @@ package io.legado.app.ui.widget.components.menuItem
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.MaterialExpressiveTheme
|
import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
@@ -15,8 +17,11 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
||||||
|
import top.yukonga.miuix.kmp.basic.ListPopupColumn
|
||||||
|
import top.yukonga.miuix.kmp.extra.SuperListPopup
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -24,31 +29,50 @@ fun RoundDropdownMenu(
|
|||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
// M3 专属视觉参数,Miuix 模式下会优雅降级(忽略)
|
||||||
shape: Shape = MaterialTheme.shapes.medium,
|
shape: Shape = MaterialTheme.shapes.medium,
|
||||||
shadowElevation: Dp = 4.dp,
|
shadowElevation: Dp = 4.dp,
|
||||||
verticalSpacing: Dp = 8.dp,
|
verticalSpacing: Dp = 8.dp,
|
||||||
content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
|
content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = rememberOpaqueColorScheme()
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
|
|
||||||
DropdownMenu(
|
if (isMiuix) {
|
||||||
expanded = expanded,
|
SuperListPopup(
|
||||||
onDismissRequest = onDismissRequest,
|
show = expanded,
|
||||||
modifier = modifier,
|
onDismissRequest = onDismissRequest,
|
||||||
shape = shape,
|
popupModifier = modifier
|
||||||
shadowElevation = shadowElevation,
|
|
||||||
containerColor = colorScheme.surfaceContainerLow
|
|
||||||
) {
|
|
||||||
MaterialExpressiveTheme(
|
|
||||||
colorScheme = colorScheme,
|
|
||||||
typography = Typography(),
|
|
||||||
motionScheme = MotionScheme.expressive(),
|
|
||||||
shapes = Shapes()
|
|
||||||
) {
|
) {
|
||||||
Column(
|
ListPopupColumn {
|
||||||
verticalArrangement = Arrangement.spacedBy(verticalSpacing)
|
Column() {
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
content(onDismissRequest)
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val colorScheme = rememberOpaqueColorScheme()
|
||||||
|
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
modifier = modifier,
|
||||||
|
shape = shape,
|
||||||
|
shadowElevation = shadowElevation,
|
||||||
|
containerColor = colorScheme.surfaceContainerLow
|
||||||
|
) {
|
||||||
|
MaterialExpressiveTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography(),
|
||||||
|
motionScheme = MotionScheme.expressive(),
|
||||||
|
shapes = Shapes()
|
||||||
) {
|
) {
|
||||||
content(onDismissRequest)
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(verticalSpacing)
|
||||||
|
) {
|
||||||
|
content(onDismissRequest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-24
@@ -1,6 +1,8 @@
|
|||||||
package io.legado.app.ui.widget.components.menuItem
|
package io.legado.app.ui.widget.components.menuItem
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -11,54 +13,114 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.MenuDefaults
|
import androidx.compose.material3.MenuDefaults
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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.ThemeResolver
|
||||||
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcon
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoundDropdownMenuItem(
|
fun RoundDropdownMenuItem(
|
||||||
text: @Composable () -> Unit,
|
text: String,
|
||||||
|
color: Color = LegadoTheme.colorScheme.surface,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
isSelected: Boolean = false,
|
||||||
leadingIcon: @Composable (() -> Unit)? = null,
|
leadingIcon: @Composable (() -> Unit)? = null,
|
||||||
trailingIcon: @Composable (() -> Unit)? = null,
|
trailingIcon: @Composable (() -> Unit)? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
|
contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
|
||||||
interactionSource: MutableInteractionSource? = null,
|
interactionSource: MutableInteractionSource? = null,
|
||||||
) {
|
) {
|
||||||
val colorScheme = rememberOpaqueColorScheme()
|
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
|
||||||
val interaction = interactionSource ?: remember { MutableInteractionSource() }
|
val interaction = interactionSource ?: remember { MutableInteractionSource() }
|
||||||
|
|
||||||
val contentColor =
|
if (isMiuix) {
|
||||||
if (enabled) MaterialTheme.colorScheme.onSurface
|
val backgroundColor = if (isSelected) {
|
||||||
else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
|
LegadoTheme.colorScheme.primaryContainer
|
||||||
|
} else {
|
||||||
|
Color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
Surface(
|
val contentColor = if (isSelected) {
|
||||||
onClick = onClick,
|
LegadoTheme.colorScheme.onPrimaryContainer
|
||||||
modifier = modifier
|
} else {
|
||||||
.padding(horizontal = 8.dp)
|
LegadoTheme.colorScheme.onSurface
|
||||||
.fillMaxWidth(),
|
}
|
||||||
enabled = enabled,
|
|
||||||
shape = MaterialTheme.shapes.small,
|
Row(
|
||||||
color = colorScheme.surface,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
interactionSource = interaction
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
modifier = modifier
|
||||||
CompositionLocalProvider(
|
.fillMaxWidth()
|
||||||
LocalContentColor provides contentColor,
|
.drawBehind { drawRect(backgroundColor) }
|
||||||
LocalTextStyle provides LegadoTheme.typography.labelLarge
|
.clickable(
|
||||||
|
interactionSource = interaction,
|
||||||
|
indication = androidx.compose.foundation.LocalIndication.current,
|
||||||
|
enabled = enabled,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
.padding(horizontal = 20.dp, vertical = 12.dp)
|
||||||
|
) {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides contentColor,
|
||||||
|
LocalTextStyle provides MiuixTheme.textStyles.body1
|
||||||
|
) {
|
||||||
|
if (leadingIcon != null) {
|
||||||
|
leadingIcon()
|
||||||
|
Spacer(Modifier.width(12.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
MiuixText(
|
||||||
|
text = text,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailingIcon != null) {
|
||||||
|
Spacer(Modifier.width(12.dp))
|
||||||
|
trailingIcon()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val colorScheme = rememberOpaqueColorScheme()
|
||||||
|
val contentColor = if (enabled) {
|
||||||
|
MaterialTheme.colorScheme.onSurface
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
|
||||||
|
}
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
enabled = enabled,
|
||||||
|
shape = MaterialTheme.shapes.small,
|
||||||
|
color = colorScheme.surface,
|
||||||
|
interactionSource = interaction
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -67,17 +129,20 @@ fun RoundDropdownMenuItem(
|
|||||||
.widthIn(min = 120.dp),
|
.widthIn(min = 120.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (leadingIcon != null) {
|
if (leadingIcon != null) {
|
||||||
leadingIcon()
|
leadingIcon()
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
contentAlignment = Alignment.CenterStart
|
contentAlignment = Alignment.CenterStart
|
||||||
) {
|
) {
|
||||||
text()
|
Text(
|
||||||
|
modifier = Modifier.widthIn(max = 200.dp),
|
||||||
|
text = text,
|
||||||
|
style = LegadoTheme.typography.bodyMediumEmphasized
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trailingIcon != null) {
|
if (trailingIcon != null) {
|
||||||
@@ -94,9 +159,9 @@ fun MenuItemIcon(
|
|||||||
imageVector: ImageVector,
|
imageVector: ImageVector,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentDescription: String? = null,
|
contentDescription: String? = null,
|
||||||
tint: Color = LocalContentColor.current
|
tint: Color = Color.Unspecified
|
||||||
) {
|
) {
|
||||||
Icon(
|
AppIcon(
|
||||||
imageVector = imageVector,
|
imageVector = imageVector,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = modifier.size(18.dp),
|
modifier = modifier.size(18.dp),
|
||||||
|
|||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
package io.legado.app.ui.widget.components.modalBottomSheet
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.BottomSheetDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.MotionScheme
|
||||||
|
import androidx.compose.material3.Shapes
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalWindowInfo
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.DpSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
||||||
|
import top.yukonga.miuix.kmp.extra.WindowBottomSheet
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AppModalBottomSheet(
|
||||||
|
show: Boolean,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
title: String? = null,
|
||||||
|
startAction: @Composable (() -> Unit)? = null,
|
||||||
|
endAction: @Composable (() -> Unit)? = null,
|
||||||
|
containerColor: Color = LegadoTheme.colorScheme.surfaceContainerLow,
|
||||||
|
content: @Composable ColumnScope.() -> Unit
|
||||||
|
) {
|
||||||
|
if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
|
||||||
|
WindowBottomSheet(
|
||||||
|
show = show,
|
||||||
|
modifier = modifier,
|
||||||
|
title = title,
|
||||||
|
startAction = startAction,
|
||||||
|
endAction = endAction,
|
||||||
|
insideMargin = DpSize(16.dp, 12.dp),
|
||||||
|
backgroundColor = containerColor,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onDismissFinished = onDismissRequest,
|
||||||
|
enableWindowDim = true,
|
||||||
|
allowDismiss = true
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (show) {
|
||||||
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
val colorScheme = rememberOpaqueColorScheme()
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val maxHeight = with(density) {
|
||||||
|
LocalWindowInfo.current.containerSize.height.toDp() * 0.8f
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
sheetState = sheetState,
|
||||||
|
containerColor = containerColor,
|
||||||
|
contentColor = colorScheme.onSurface,
|
||||||
|
dragHandle = { BottomSheetDefaults.DragHandle() }
|
||||||
|
) {
|
||||||
|
MaterialExpressiveTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography(),
|
||||||
|
motionScheme = MotionScheme.expressive(),
|
||||||
|
shapes = Shapes()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.heightIn(max = maxHeight)
|
||||||
|
.animateContentSize()
|
||||||
|
.then(modifier)
|
||||||
|
) {
|
||||||
|
val hasHeader =
|
||||||
|
!title.isNullOrEmpty() || startAction != null || endAction != null
|
||||||
|
|
||||||
|
if (hasHeader) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 16.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
if (startAction != null) {
|
||||||
|
Box(modifier = Modifier.align(Alignment.CenterStart)) {
|
||||||
|
startAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!title.isNullOrEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = LegadoTheme.typography.titleMediumEmphasized,
|
||||||
|
color = colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.padding(horizontal = 56.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endAction != null) {
|
||||||
|
Box(modifier = Modifier.align(Alignment.CenterEnd)) {
|
||||||
|
endAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-64
@@ -1,64 +0,0 @@
|
|||||||
package io.legado.app.ui.widget.components.modalBottomSheet
|
|
||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.material3.ColorScheme
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|
||||||
import androidx.compose.material3.MaterialExpressiveTheme
|
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
|
||||||
import androidx.compose.material3.MotionScheme
|
|
||||||
import androidx.compose.material3.Shapes
|
|
||||||
import androidx.compose.material3.SheetState
|
|
||||||
import androidx.compose.material3.Typography
|
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.platform.LocalWindowInfo
|
|
||||||
import io.legado.app.ui.theme.rememberOpaqueColorScheme
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
|
||||||
@Composable
|
|
||||||
fun GlassModalBottomSheet(
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
sheetState: SheetState = rememberModalBottomSheetState(
|
|
||||||
skipPartiallyExpanded = true
|
|
||||||
),
|
|
||||||
containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow },
|
|
||||||
content: @Composable ColumnScope.() -> Unit
|
|
||||||
) {
|
|
||||||
val colorScheme = rememberOpaqueColorScheme()
|
|
||||||
val density = LocalDensity.current
|
|
||||||
val maxHeight = with(density) {
|
|
||||||
LocalWindowInfo.current.containerSize.height.toDp() * 0.8f
|
|
||||||
}
|
|
||||||
|
|
||||||
ModalBottomSheet(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
sheetState = sheetState,
|
|
||||||
containerColor = containerColor(colorScheme),
|
|
||||||
contentColor = colorScheme.onSurface
|
|
||||||
) {
|
|
||||||
MaterialExpressiveTheme(
|
|
||||||
colorScheme = colorScheme,
|
|
||||||
typography = Typography(),
|
|
||||||
motionScheme = MotionScheme.expressive(),
|
|
||||||
shapes = Shapes()
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.heightIn(max = maxHeight)
|
|
||||||
.animateContentSize()
|
|
||||||
.then(modifier),
|
|
||||||
content = content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,7 @@ 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.widget.components.modalBottomSheet.GlassModalBottomSheet
|
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
|
||||||
|
|
||||||
@@ -52,6 +52,7 @@ data class RuleEditFields(
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> RuleEditSheet(
|
fun <T> RuleEditSheet(
|
||||||
|
show: Boolean,
|
||||||
rule: T?,
|
rule: T?,
|
||||||
title: String,
|
title: String,
|
||||||
label1: String,
|
label1: String,
|
||||||
@@ -74,7 +75,8 @@ fun <T> RuleEditSheet(
|
|||||||
|
|
||||||
fun getCurrentEntity() = fromFields(RuleEditFields(name, rule1, rule2), rule)
|
fun getCurrentEntity() = fromFields(RuleEditFields(name, rule1, rule2), rule)
|
||||||
|
|
||||||
GlassModalBottomSheet(
|
AppModalBottomSheet(
|
||||||
|
show = show,
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.fillMaxWidth()) {
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
|||||||
+167
-110
@@ -1,13 +1,12 @@
|
|||||||
package io.legado.app.ui.widget.components.settingItem
|
package io.legado.app.ui.widget.components.settingItem
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Remove
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -20,17 +19,22 @@ import androidx.compose.runtime.mutableFloatStateOf
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconButton
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
||||||
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
|
import io.legado.app.ui.widget.components.ValueStepper
|
||||||
import io.legado.app.ui.widget.components.card.TextCard
|
import io.legado.app.ui.widget.components.card.TextCard
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import top.yukonga.miuix.kmp.basic.BasicComponent
|
||||||
|
import top.yukonga.miuix.kmp.extra.SuperSwitch
|
||||||
|
import top.yukonga.miuix.kmp.extra.WindowDropdown
|
||||||
|
import top.yukonga.miuix.kmp.basic.Slider as MiuixSlider
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -41,49 +45,73 @@ fun CompactDropdownSettingItem(
|
|||||||
entryValues: Array<String>,
|
entryValues: Array<String>,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
imageVector: ImageVector? = null,
|
imageVector: ImageVector? = null,
|
||||||
color: Color? = MaterialTheme.colorScheme.surface,
|
color: Color? = LegadoTheme.colorScheme.surface,
|
||||||
shape: Shape = MaterialTheme.shapes.small,
|
shape: Shape = MaterialTheme.shapes.small,
|
||||||
onValueChange: (String) -> Unit
|
onValueChange: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val currentEntry = displayEntries.getOrNull(entryValues.indexOf(selectedValue)) ?: selectedValue
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
|
val selectedIndex = entryValues.indexOf(selectedValue).coerceAtLeast(0)
|
||||||
|
val spinnerItems = displayEntries.toList()
|
||||||
|
|
||||||
SettingItem(
|
WindowDropdown(
|
||||||
title = title,
|
title = title,
|
||||||
description = description,
|
summary = description,
|
||||||
imageVector = imageVector,
|
items = spinnerItems,
|
||||||
color = color,
|
selectedIndex = selectedIndex,
|
||||||
shape = shape,
|
startAction = imageVector?.let { icon ->
|
||||||
trailingContent = {
|
{
|
||||||
TextCard(
|
Icon(
|
||||||
cornerRadius = 8.dp,
|
imageVector = icon,
|
||||||
horizontalPadding = 8.dp,
|
contentDescription = null
|
||||||
verticalPadding = 4.dp,
|
)
|
||||||
text = currentEntry,
|
}
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
|
},
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface
|
onSelectedIndexChange = { index ->
|
||||||
)
|
onValueChange(entryValues[index])
|
||||||
},
|
|
||||||
dropdownMenu = { onDismiss ->
|
|
||||||
displayEntries.forEachIndexed { index, display ->
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = { AppText(display) },
|
|
||||||
onClick = {
|
|
||||||
onValueChange(entryValues[index])
|
|
||||||
onDismiss()
|
|
||||||
},
|
|
||||||
trailingIcon = if (selectedValue == entryValues[index]) {
|
|
||||||
{
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Check,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(18.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
} else {
|
||||||
|
val currentEntry =
|
||||||
|
displayEntries.getOrNull(entryValues.indexOf(selectedValue)) ?: selectedValue
|
||||||
|
|
||||||
|
SettingItem(
|
||||||
|
title = title,
|
||||||
|
description = description,
|
||||||
|
imageVector = imageVector,
|
||||||
|
color = color,
|
||||||
|
shape = shape,
|
||||||
|
trailingContent = {
|
||||||
|
TextCard(
|
||||||
|
cornerRadius = 8.dp,
|
||||||
|
horizontalPadding = 8.dp,
|
||||||
|
verticalPadding = 4.dp,
|
||||||
|
text = currentEntry,
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
},
|
||||||
|
dropdownMenu = { onDismiss ->
|
||||||
|
displayEntries.forEachIndexed { index, display ->
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = display,
|
||||||
|
onClick = {
|
||||||
|
onValueChange(entryValues[index])
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
trailingIcon = if (selectedValue == entryValues[index]) {
|
||||||
|
{
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Check,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@@ -104,59 +132,78 @@ fun CompactSliderSettingItem(
|
|||||||
var sliderValue by remember(value) { mutableFloatStateOf(value) }
|
var sliderValue by remember(value) { mutableFloatStateOf(value) }
|
||||||
var displayValue by remember(value) { mutableFloatStateOf(value) }
|
var displayValue by remember(value) { mutableFloatStateOf(value) }
|
||||||
|
|
||||||
SettingItem(
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
title = title,
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
description = description,
|
BasicComponent(
|
||||||
imageVector = imageVector,
|
title = title,
|
||||||
color = color,
|
summary = description,
|
||||||
shape = shape,
|
onClick = { expanded = !expanded },
|
||||||
expanded = expanded,
|
endActions = {
|
||||||
onExpandChange = { expanded = it },
|
ValueStepper(
|
||||||
trailingContent = {
|
value = value,
|
||||||
Row(
|
displayValue = displayValue,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
valueRange = valueRange,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
onValueChange = onValueChange
|
||||||
) {
|
)
|
||||||
SmallOutlinedIconButton(
|
}
|
||||||
onClick = {
|
)
|
||||||
val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
|
|
||||||
onValueChange(newValue)
|
AnimatedVisibility(visible = expanded) {
|
||||||
},
|
Column(
|
||||||
icon = Icons.Default.Remove
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
MiuixSlider(
|
||||||
|
value = sliderValue,
|
||||||
|
onValueChange = {
|
||||||
|
sliderValue = it
|
||||||
|
displayValue = it
|
||||||
|
},
|
||||||
|
onValueChangeFinished = {
|
||||||
|
onValueChange(sliderValue)
|
||||||
|
},
|
||||||
|
valueRange = valueRange,
|
||||||
|
steps = steps,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SettingItem(
|
||||||
|
title = title,
|
||||||
|
description = description,
|
||||||
|
imageVector = imageVector,
|
||||||
|
color = color,
|
||||||
|
shape = shape,
|
||||||
|
expanded = expanded,
|
||||||
|
onExpandChange = { expanded = it },
|
||||||
|
trailingContent = {
|
||||||
|
ValueStepper(
|
||||||
|
value = value,
|
||||||
|
displayValue = displayValue,
|
||||||
|
valueRange = valueRange,
|
||||||
|
onValueChange = onValueChange
|
||||||
)
|
)
|
||||||
TextCard(
|
},
|
||||||
cornerRadius = 8.dp,
|
expandContent = {
|
||||||
horizontalPadding = 8.dp,
|
Slider(
|
||||||
verticalPadding = 4.dp,
|
value = sliderValue,
|
||||||
text = displayValue.toInt().toString(),
|
onValueChange = {
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
|
sliderValue = it
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface
|
displayValue = it
|
||||||
)
|
|
||||||
SmallOutlinedIconButton(
|
|
||||||
onClick = {
|
|
||||||
val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
|
|
||||||
onValueChange(newValue)
|
|
||||||
},
|
},
|
||||||
icon = Icons.Default.Add
|
onValueChangeFinished = {
|
||||||
|
onValueChange(sliderValue)
|
||||||
|
},
|
||||||
|
valueRange = valueRange,
|
||||||
|
steps = steps,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
)
|
||||||
expandContent = {
|
}
|
||||||
Slider(
|
|
||||||
value = sliderValue,
|
|
||||||
onValueChange = {
|
|
||||||
sliderValue = it
|
|
||||||
displayValue = it
|
|
||||||
},
|
|
||||||
onValueChangeFinished = {
|
|
||||||
onValueChange(sliderValue)
|
|
||||||
},
|
|
||||||
valueRange = valueRange,
|
|
||||||
steps = steps,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
LaunchedEffect(value) {
|
LaunchedEffect(value) {
|
||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
@@ -177,20 +224,30 @@ fun CompactSwitchSettingItem(
|
|||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onCheckedChange: (Boolean) -> Unit
|
onCheckedChange: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
SettingItem(
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
title = title,
|
SuperSwitch(
|
||||||
description = description,
|
title = title,
|
||||||
imageVector = imageVector,
|
summary = description,
|
||||||
color = color,
|
checked = checked,
|
||||||
shape = shape,
|
onCheckedChange = onCheckedChange,
|
||||||
onClick = { if (enabled) onCheckedChange(!checked) },
|
enabled = enabled,
|
||||||
trailingContent = {
|
)
|
||||||
Switch(
|
} else {
|
||||||
modifier = Modifier.scale(0.8f),
|
SettingItem(
|
||||||
checked = checked,
|
title = title,
|
||||||
onCheckedChange = onCheckedChange,
|
description = description,
|
||||||
enabled = enabled
|
imageVector = imageVector,
|
||||||
)
|
color = color,
|
||||||
}
|
shape = shape,
|
||||||
)
|
onClick = { if (enabled) onCheckedChange(!checked) },
|
||||||
|
trailingContent = {
|
||||||
|
Switch(
|
||||||
|
modifier = Modifier.scale(0.8f),
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
|
enabled = enabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ 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.ThemeResolver
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
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 top.yukonga.miuix.kmp.basic.SpinnerEntry
|
import top.yukonga.miuix.kmp.basic.SpinnerEntry
|
||||||
import top.yukonga.miuix.kmp.extra.SuperSpinner
|
import top.yukonga.miuix.kmp.extra.SuperSpinner
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@ fun DropdownListSettingItem(
|
|||||||
dropdownMenu = { onDismiss ->
|
dropdownMenu = { onDismiss ->
|
||||||
displayEntries.forEachIndexed { index, display ->
|
displayEntries.forEachIndexed { index, display ->
|
||||||
RoundDropdownMenuItem(
|
RoundDropdownMenuItem(
|
||||||
text = { AppText(display) },
|
text = display,
|
||||||
onClick = {
|
onClick = {
|
||||||
onValueChange(entryValues[index])
|
onValueChange(entryValues[index])
|
||||||
onDismiss()
|
onDismiss()
|
||||||
|
|||||||
+4
-15
@@ -6,8 +6,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
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.widget.components.AdaptiveSwitch
|
import io.legado.app.ui.widget.components.AdaptiveSwitch
|
||||||
import top.yukonga.miuix.kmp.basic.BasicComponent
|
import top.yukonga.miuix.kmp.extra.SuperSwitch
|
||||||
import top.yukonga.miuix.kmp.basic.Switch
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -23,22 +22,12 @@ fun SwitchSettingItem(
|
|||||||
val composeEngine = LegadoTheme.composeEngine
|
val composeEngine = LegadoTheme.composeEngine
|
||||||
|
|
||||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
||||||
val toggleAction = {
|
SuperSwitch(
|
||||||
if (enabled) onCheckedChange(!checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicComponent(
|
|
||||||
title = title,
|
title = title,
|
||||||
summary = description,
|
summary = description,
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
onClick = toggleAction,
|
|
||||||
endActions = {
|
|
||||||
Switch(
|
|
||||||
checked = checked,
|
|
||||||
onCheckedChange = onCheckedChange,
|
|
||||||
enabled = enabled
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SettingItem(
|
SettingItem(
|
||||||
|
|||||||
+3
-3
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PrimaryScrollableTabRow
|
import androidx.compose.material3.PrimaryScrollableTabRow
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -11,12 +12,11 @@ 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.ThemeResolver
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
import top.yukonga.miuix.kmp.basic.TabRowDefaults
|
import top.yukonga.miuix.kmp.basic.TabRowDefaults
|
||||||
import top.yukonga.miuix.kmp.basic.TabRowWithContour
|
import top.yukonga.miuix.kmp.basic.TabRowWithContour
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AdaptiveTabRow(
|
fun AppTabRow(
|
||||||
tabTitles: List<String>,
|
tabTitles: List<String>,
|
||||||
selectedTabIndex: Int,
|
selectedTabIndex: Int,
|
||||||
onTabSelected: (Int) -> Unit,
|
onTabSelected: (Int) -> Unit,
|
||||||
@@ -48,7 +48,7 @@ fun AdaptiveTabRow(
|
|||||||
selected = selectedTabIndex == index,
|
selected = selectedTabIndex == index,
|
||||||
onClick = { onTabSelected(index) },
|
onClick = { onTabSelected(index) },
|
||||||
text = {
|
text = {
|
||||||
AppText(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
+1
-2
@@ -1,4 +1,4 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components.text
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@@ -22,7 +22,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnimatedText(
|
fun AnimatedText(
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package io.legado.app.ui.widget.components.text
|
package io.legado.app.ui.widget.components.text
|
||||||
|
|
||||||
|
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.takeOrElse
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.TextLayoutResult
|
import androidx.compose.ui.text.TextLayoutResult
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
@@ -16,10 +16,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import io.legado.app.ui.theme.LegadoTheme.composeEngine
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import io.legado.app.ui.theme.ThemeResolver
|
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
|
||||||
import top.yukonga.miuix.kmp.basic.Text as MiuixText
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppText(
|
fun AppText(
|
||||||
@@ -41,53 +38,41 @@ fun AppText(
|
|||||||
onTextLayout: ((TextLayoutResult) -> Unit)? = null,
|
onTextLayout: ((TextLayoutResult) -> Unit)? = null,
|
||||||
style: TextStyle? = null,
|
style: TextStyle? = null,
|
||||||
) {
|
) {
|
||||||
|
val baseStyle = style ?: LegadoTheme.typography.bodyMedium
|
||||||
|
|
||||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
// 2. 获取默认文本色:直接拿你自己封装的 LegadoTheme.colorScheme.onSurface
|
||||||
MiuixText(
|
val defaultTextColor = LegadoTheme.colorScheme.onSurface
|
||||||
text = text,
|
|
||||||
modifier = modifier,
|
// 3. 颜色降级逻辑:传入的 color -> style 中的 color -> 主题默认色
|
||||||
color = color,
|
val finalTextColor = color.takeOrElse {
|
||||||
fontSize = fontSize,
|
baseStyle.color.takeOrElse { defaultTextColor }
|
||||||
fontStyle = fontStyle,
|
|
||||||
fontWeight = fontWeight,
|
|
||||||
fontFamily = fontFamily,
|
|
||||||
letterSpacing = letterSpacing,
|
|
||||||
textDecoration = textDecoration,
|
|
||||||
textAlign = textAlign,
|
|
||||||
lineHeight = lineHeight,
|
|
||||||
overflow = overflow,
|
|
||||||
softWrap = softWrap,
|
|
||||||
maxLines = maxLines,
|
|
||||||
minLines = minLines,
|
|
||||||
onTextLayout = onTextLayout,
|
|
||||||
style = style ?: MiuixTheme.textStyles.main
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
modifier = modifier,
|
|
||||||
color = color,
|
|
||||||
fontSize = fontSize,
|
|
||||||
fontStyle = fontStyle,
|
|
||||||
fontWeight = fontWeight,
|
|
||||||
fontFamily = fontFamily,
|
|
||||||
letterSpacing = letterSpacing,
|
|
||||||
textDecoration = textDecoration,
|
|
||||||
textAlign = textAlign,
|
|
||||||
lineHeight = lineHeight,
|
|
||||||
overflow = overflow,
|
|
||||||
softWrap = softWrap,
|
|
||||||
maxLines = maxLines,
|
|
||||||
minLines = minLines,
|
|
||||||
onTextLayout = onTextLayout ?: {},
|
|
||||||
style = style ?: LocalTextStyle.current
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BasicText(
|
||||||
|
text = text,
|
||||||
|
modifier = modifier,
|
||||||
|
style = baseStyle.merge(
|
||||||
|
color = finalTextColor,
|
||||||
|
fontSize = fontSize,
|
||||||
|
fontWeight = fontWeight,
|
||||||
|
textAlign = textAlign ?: TextAlign.Unspecified,
|
||||||
|
lineHeight = lineHeight,
|
||||||
|
fontFamily = fontFamily,
|
||||||
|
textDecoration = textDecoration,
|
||||||
|
fontStyle = fontStyle,
|
||||||
|
letterSpacing = letterSpacing,
|
||||||
|
),
|
||||||
|
onTextLayout = onTextLayout,
|
||||||
|
overflow = overflow,
|
||||||
|
softWrap = softWrap,
|
||||||
|
maxLines = maxLines,
|
||||||
|
minLines = minLines
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppText(
|
fun AppText(
|
||||||
text: AnnotatedString,
|
text: AnnotatedString, // 接收 AnnotatedString
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
color: Color = Color.Unspecified,
|
color: Color = Color.Unspecified,
|
||||||
fontSize: TextUnit = TextUnit.Unspecified,
|
fontSize: TextUnit = TextUnit.Unspecified,
|
||||||
@@ -105,46 +90,36 @@ fun AppText(
|
|||||||
onTextLayout: ((TextLayoutResult) -> Unit)? = null,
|
onTextLayout: ((TextLayoutResult) -> Unit)? = null,
|
||||||
style: TextStyle? = null,
|
style: TextStyle? = null,
|
||||||
) {
|
) {
|
||||||
|
// 1. 获取基础样式:如果外部没传 style,直接拿你自己封装的 LegadoTheme.typography.bodyMedium
|
||||||
|
// 这完美避开了 M3 的 LocalTextStyle,且自动适配 Miuix/M3 引擎!
|
||||||
|
val baseStyle = style ?: LegadoTheme.typography.bodyMedium
|
||||||
|
|
||||||
if (ThemeResolver.isMiuixEngine(composeEngine)) {
|
// 2. 获取默认文本色:直接拿你自己封装的 LegadoTheme.colorScheme.onSurface
|
||||||
MiuixText(
|
val defaultTextColor = LegadoTheme.colorScheme.onSurface
|
||||||
text = text,
|
|
||||||
modifier = modifier,
|
// 3. 颜色降级逻辑:传入的 color -> style 中的 color -> 主题默认色
|
||||||
color = color,
|
val finalTextColor = color.takeOrElse {
|
||||||
fontSize = fontSize,
|
baseStyle.color.takeOrElse { defaultTextColor }
|
||||||
fontStyle = fontStyle,
|
|
||||||
fontWeight = fontWeight,
|
|
||||||
fontFamily = fontFamily,
|
|
||||||
letterSpacing = letterSpacing,
|
|
||||||
textDecoration = textDecoration,
|
|
||||||
textAlign = textAlign,
|
|
||||||
lineHeight = lineHeight,
|
|
||||||
overflow = overflow,
|
|
||||||
softWrap = softWrap,
|
|
||||||
maxLines = maxLines,
|
|
||||||
minLines = minLines,
|
|
||||||
onTextLayout = onTextLayout ?: {},
|
|
||||||
style = style ?: MiuixTheme.textStyles.main
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
modifier = modifier,
|
|
||||||
color = color,
|
|
||||||
fontSize = fontSize,
|
|
||||||
fontStyle = fontStyle,
|
|
||||||
fontWeight = fontWeight,
|
|
||||||
fontFamily = fontFamily,
|
|
||||||
letterSpacing = letterSpacing,
|
|
||||||
textDecoration = textDecoration,
|
|
||||||
textAlign = textAlign,
|
|
||||||
lineHeight = lineHeight,
|
|
||||||
overflow = overflow,
|
|
||||||
softWrap = softWrap,
|
|
||||||
maxLines = maxLines,
|
|
||||||
minLines = minLines,
|
|
||||||
onTextLayout = onTextLayout ?: {},
|
|
||||||
style = style ?: LocalTextStyle.current
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BasicText(
|
||||||
|
text = text,
|
||||||
|
modifier = modifier,
|
||||||
|
style = baseStyle.merge(
|
||||||
|
color = finalTextColor,
|
||||||
|
fontSize = fontSize,
|
||||||
|
fontWeight = fontWeight,
|
||||||
|
textAlign = textAlign ?: TextAlign.Unspecified,
|
||||||
|
lineHeight = lineHeight,
|
||||||
|
fontFamily = fontFamily,
|
||||||
|
textDecoration = textDecoration,
|
||||||
|
fontStyle = fontStyle,
|
||||||
|
letterSpacing = letterSpacing,
|
||||||
|
),
|
||||||
|
onTextLayout = onTextLayout,
|
||||||
|
overflow = overflow,
|
||||||
|
softWrap = softWrap,
|
||||||
|
maxLines = maxLines,
|
||||||
|
minLines = minLines
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,9 @@ import androidx.compose.foundation.layout.ColumnScope
|
|||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.Close
|
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -23,12 +20,13 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import io.legado.app.ui.widget.components.AnimatedTextLine
|
|
||||||
import io.legado.app.ui.widget.components.SearchBarSection
|
import io.legado.app.ui.widget.components.SearchBarSection
|
||||||
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
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.icon.AppIcons
|
||||||
import io.legado.app.ui.widget.components.list.ListUiState
|
import io.legado.app.ui.widget.components.list.ListUiState
|
||||||
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.text.AnimatedTextLine
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -70,7 +68,7 @@ fun <T> DynamicTopAppBar(
|
|||||||
if (isSelecting || onBackClick != null) {
|
if (isSelecting || onBackClick != null) {
|
||||||
TopbarNavigationButton(
|
TopbarNavigationButton(
|
||||||
onClick = { if (isSelecting) onClearSelection() else onBackClick?.invoke() },
|
onClick = { if (isSelecting) onClearSelection() else onBackClick?.invoke() },
|
||||||
imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
|
imageVector = if (isSelecting) AppIcons.Close else AppIcons.Back,
|
||||||
contentDescription = if (isSelecting) "取消选择" else "返回"
|
contentDescription = if (isSelecting) "取消选择" else "返回"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -113,7 +111,6 @@ fun <T> DynamicTopAppBar(
|
|||||||
query = state.searchKey,
|
query = state.searchKey,
|
||||||
onQueryChange = onSearchQueryChange,
|
onQueryChange = onSearchQueryChange,
|
||||||
placeholder = searchPlaceholder,
|
placeholder = searchPlaceholder,
|
||||||
leadingIcon = { Icon(searchLeadingIcon, null) },
|
|
||||||
trailingIcon = searchTrailingIcon,
|
trailingIcon = searchTrailingIcon,
|
||||||
dropdownMenu = searchDropdownMenu
|
dropdownMenu = searchDropdownMenu
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -21,8 +21,8 @@ import io.legado.app.ui.theme.LegadoTheme
|
|||||||
import io.legado.app.ui.theme.LocalHazeState
|
import io.legado.app.ui.theme.LocalHazeState
|
||||||
import io.legado.app.ui.theme.ThemeResolver
|
import io.legado.app.ui.theme.ThemeResolver
|
||||||
import io.legado.app.ui.theme.responsiveHazeEffect
|
import io.legado.app.ui.theme.responsiveHazeEffect
|
||||||
import io.legado.app.ui.widget.components.AdaptiveAnimatedText
|
|
||||||
import io.legado.app.ui.widget.components.GlassDefaults
|
import io.legado.app.ui.widget.components.GlassDefaults
|
||||||
|
import io.legado.app.ui.widget.components.text.AdaptiveAnimatedText
|
||||||
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
|
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
import top.yukonga.miuix.kmp.basic.TopAppBar as MiuixTopAppBar
|
import top.yukonga.miuix.kmp.basic.TopAppBar as MiuixTopAppBar
|
||||||
|
|||||||
Reference in new issue
Block a user