feat: 优化设置页页面样式,简化布局

This commit is contained in:
Super12138 2026-01-18 11:48:44 +08:00
parent 12285e5be7
commit 667cb152ea
26 changed files with 510 additions and 418 deletions

View file

@ -38,7 +38,7 @@ android {
applicationId = "cn.super12138.todo" applicationId = "cn.super12138.todo"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 923 versionCode = 940
versionName = "2.3.3" versionName = "2.3.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

View file

@ -1,5 +1,11 @@
package cn.super12138.todo.ui package cn.super12138.todo.ui
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
object TodoDefaults { object TodoDefaults {
@ -26,7 +32,9 @@ object TodoDefaults {
/** /**
* 设置项垂直边距 * 设置项垂直边距
*/ */
val settingsItemVerticalPadding = 20.dp val settingsItemVerticalPadding = 16.dp
val settingsItemPadding = 2.dp
/** /**
* 待办进度条粗度 * 待办进度条粗度
@ -47,4 +55,29 @@ object TodoDefaults {
* 待办进度条波幅 * 待办进度条波幅
*/ */
const val waveAmplitude = 0.6f const val waveAmplitude = 0.6f
val ScreenContainerShape: Shape
@Composable
get() = MaterialTheme.shapes.large.copy(
bottomStart = ZeroCornerSize,
bottomEnd = ZeroCornerSize
)
val ContainerColor: Color
@Composable
get() = MaterialTheme.colorScheme.surfaceBright
val BackgroundColor: Color
@Composable
get() = MaterialTheme.colorScheme.surfaceContainer
val SettingsItemDefaultShape: CornerBasedShape
@Composable
get() = MaterialTheme.shapes.small
val SettingsItemRoundedShape: CornerBasedShape
@Composable
get() = MaterialTheme.shapes.large
val fadedEdgeWidth = 8.dp
} }

View file

@ -73,7 +73,7 @@ class CrashActivity : ComponentActivity() {
) { ) {
CrashPage( CrashPage(
crashLog = crashLogs ?: stringResource(R.string.tip_no_crash_logs), crashLog = crashLogs ?: stringResource(R.string.tip_no_crash_logs),
exitApp = { finishAffinity() }, exitApp = ::finishAffinity,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
} }

View file

@ -8,7 +8,6 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -19,6 +18,7 @@ import androidx.core.view.WindowCompat
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import cn.super12138.todo.constants.Constants import cn.super12138.todo.constants.Constants
import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.datastore.DataStoreManager
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.Konfetti import cn.super12138.todo.ui.components.Konfetti
import cn.super12138.todo.ui.navigation.TodoNavigation import cn.super12138.todo.ui.navigation.TodoNavigation
import cn.super12138.todo.ui.theme.DarkMode import cn.super12138.todo.ui.theme.DarkMode
@ -83,7 +83,7 @@ class MainActivity : ComponentActivity() {
contrastLevel = contrastLevel.toDouble(), contrastLevel = contrastLevel.toDouble(),
dynamicColor = dynamicColor dynamicColor = dynamicColor
) { ) {
Surface(color = MaterialTheme.colorScheme.background) { Surface(color = TodoDefaults.BackgroundColor) {
TodoNavigation( TodoNavigation(
viewModel = mainViewModel, viewModel = mainViewModel,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()

View file

@ -4,26 +4,28 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FabPosition import androidx.compose.material3.FabPosition
import androidx.compose.material3.FilledIconButton import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.VibrationUtils import cn.super12138.todo.utils.VibrationUtils
/** /**
@ -31,16 +33,14 @@ import cn.super12138.todo.utils.VibrationUtils
* * 内容默认由 Box 容器包裹实际使用时推荐配合 Column Row * * 内容默认由 Box 容器包裹实际使用时推荐配合 Column Row
* *
* @param title 标题文本 * @param title 标题文本
* @param scrollBehavior 滚动行为用于支持页面内容滚动时标题栏的压缩效果
* @param onBack 当返回按钮被按下时的操作 * @param onBack 当返回按钮被按下时的操作
* @param contentWindowInsets 内容边距通常用于将内容和系统状态栏等隔开可以使用 `WindowInsets.safeContent` * @param contentWindowInsets 内容边距通常用于将内容和系统状态栏等隔开可以使用 `WindowInsets.safeContent`
*/ */
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun LargeTopAppBarScaffold( fun TopAppBarScaffold(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
title: String, title: String,
scrollBehavior: TopAppBarScrollBehavior,
onBack: () -> Unit, onBack: () -> Unit,
snackbarHost: @Composable () -> Unit = {}, snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {},
@ -49,7 +49,7 @@ fun LargeTopAppBarScaffold(
content: @Composable (PaddingValues) -> Unit content: @Composable (PaddingValues) -> Unit
) { ) {
val view = LocalView.current val view = LocalView.current
LargeTopAppBarScaffold( TopAppBarScaffold(
title = { title = {
Text( Text(
text = title, text = title,
@ -59,7 +59,7 @@ fun LargeTopAppBarScaffold(
}, },
navigationIcon = { navigationIcon = {
FilledIconButton( FilledIconButton(
colors = IconButtonDefaults.filledIconButtonColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh), colors = IconButtonDefaults.filledIconButtonColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHighest),
shapes = IconButtonDefaults.shapes(), shapes = IconButtonDefaults.shapes(),
onClick = { onClick = {
VibrationUtils.performHapticFeedback(view) VibrationUtils.performHapticFeedback(view)
@ -72,25 +72,24 @@ fun LargeTopAppBarScaffold(
) )
} }
}, },
scrollBehavior = scrollBehavior,
snackbarHost = snackbarHost, snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton, floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition, floatingActionButtonPosition = floatingActionButtonPosition,
contentWindowInsets = contentWindowInsets, contentWindowInsets = contentWindowInsets,
modifier = modifier modifier = modifier,
) { innerPadding -> ) { innerPadding ->
//TODO: 应用圆角效果
Box { content(innerPadding) } Box { content(innerPadding) }
} }
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun LargeTopAppBarScaffold( fun TopAppBarScaffold(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
title: @Composable () -> Unit = {}, title: @Composable () -> Unit = {},
navigationIcon: @Composable () -> Unit = {}, navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {}, actions: @Composable RowScope.() -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior,
snackbarHost: @Composable () -> Unit = {}, snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End, floatingActionButtonPosition: FabPosition = FabPosition.End,
@ -98,20 +97,22 @@ fun LargeTopAppBarScaffold(
content: @Composable (PaddingValues) -> Unit content: @Composable (PaddingValues) -> Unit
) { ) {
Scaffold( Scaffold(
modifier = modifier modifier = modifier,
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { topBar = {
LargeTopAppBar( TopAppBar(
title = title, title = title,
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actions = actions, actions = actions,
scrollBehavior = scrollBehavior colors = TopAppBarDefaults.topAppBarColors(
containerColor = TodoDefaults.BackgroundColor,
)
) )
}, },
snackbarHost = snackbarHost, snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton, floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition, floatingActionButtonPosition = floatingActionButtonPosition,
contentWindowInsets = contentWindowInsets contentWindowInsets = contentWindowInsets,
containerColor = TodoDefaults.BackgroundColor,
) { innerPadding -> ) { innerPadding ->
Box { content(innerPadding) } Box { content(innerPadding) }
} }

View file

@ -0,0 +1,23 @@
package cn.super12138.todo.ui.components
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cn.super12138.todo.ui.TodoDefaults
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun RoundedScreenContainer(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
Surface(
modifier = modifier.padding(horizontal = TodoDefaults.screenHorizontalPadding),
color = TodoDefaults.BackgroundColor,
shape = TodoDefaults.ScreenContainerShape,
content = content
)
}

View file

@ -44,7 +44,7 @@ import cn.super12138.todo.logic.datastore.DataStoreManager
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.ChipItem import cn.super12138.todo.ui.components.ChipItem
import cn.super12138.todo.ui.components.ConfirmDialog import cn.super12138.todo.ui.components.ConfirmDialog
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.components.TodoFloatingActionButton import cn.super12138.todo.ui.components.TodoFloatingActionButton
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryChip import cn.super12138.todo.ui.pages.editor.components.TodoCategoryChip
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField
@ -69,7 +69,7 @@ fun TodoEditorPage(
// TODO: 本页及其相关组件重组性能检查优化 // TODO: 本页及其相关组件重组性能检查优化
val view = LocalView.current val view = LocalView.current
val animatedVisibilityScope = LocalNavAnimatedContentScope.current val animatedVisibilityScope = LocalNavAnimatedContentScope.current
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val uiState = rememberEditorState(initialTodo = toDo) val uiState = rememberEditorState(initialTodo = toDo)
@ -112,9 +112,8 @@ fun TodoEditorPage(
BackHandler(onBack = ::checkModifiedBeforeBack) BackHandler(onBack = ::checkModifiedBeforeBack)
LargeTopAppBarScaffold( TopAppBarScaffold(
title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task), title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
scrollBehavior = scrollBehavior,
floatingActionButton = { floatingActionButton = {
with(sharedTransitionScope) { with(sharedTransitionScope) {
Row( Row(

View file

@ -1,15 +1,10 @@
package cn.super12138.todo.ui.pages.settings package cn.super12138.todo.ui.pages.settings
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -18,17 +13,15 @@ import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.remember 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.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.constants.Constants import cn.super12138.todo.constants.Constants
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.icons.GitHubIcon import cn.super12138.todo.ui.icons.GitHubIcon
import cn.super12138.todo.ui.pages.settings.components.Settings import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsItem import cn.super12138.todo.ui.pages.settings.components.SettingsItem
import cn.super12138.todo.utils.SystemUtils import cn.super12138.todo.utils.SystemUtils
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -42,80 +35,82 @@ fun SettingsAbout(
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() TopAppBarScaffold(
LargeTopAppBarScaffold(
title = stringResource(R.string.pref_about), title = stringResource(R.string.pref_about),
onBack = onNavigateUp, onBack = onNavigateUp,
scrollBehavior = scrollBehavior, modifier = modifier,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { innerPadding -> ) { innerPadding ->
val context = LocalContext.current val context = LocalContext.current
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
var clickCount by remember { mutableIntStateOf(0) }
var lastClickTime by remember { mutableLongStateOf(0L) }
Column( LaunchedEffect(clickCount) {
verticalArrangement = Arrangement.spacedBy(2.dp), if (clickCount > 0) {
modifier = Modifier lastClickTime = System.currentTimeMillis()
.fillMaxSize() val currentClickTime = lastClickTime
.padding(innerPadding) delay(300L)
.padding(horizontal = TodoDefaults.screenHorizontalPadding)
.verticalScroll(rememberScrollState())
) {
Settings {
var clickCount by remember { mutableIntStateOf(0) }
var lastClickTime by remember { mutableLongStateOf(0L) }
LaunchedEffect(clickCount) { if (currentClickTime == lastClickTime) {
if (clickCount > 0) { clickCount = 0
lastClickTime = System.currentTimeMillis()
val currentClickTime = lastClickTime
delay(300L)
if (currentClickTime == lastClickTime) {
clickCount = 0
}
}
} }
}
}
SettingsItem( RoundedScreenContainer(Modifier.padding(innerPadding)) {
leadingIconRes = R.drawable.ic_numbers, SettingsContainer(Modifier.fillMaxSize()) {
title = stringResource(R.string.pref_app_version), item {
description = SystemUtils.getAppVersion(context), SettingsItem(
onClick = { leadingIconRes = R.drawable.ic_numbers,
clickCount++ title = stringResource(R.string.pref_app_version),
if (clickCount == 5) { description = SystemUtils.getAppVersion(context),
if ((System.currentTimeMillis() % 2) == 0.toLong()) { onClick = {
Toast.makeText(context, "🍨", Toast.LENGTH_SHORT).show() clickCount++
} else { if (clickCount == 5) {
Toast.makeText(context, "✈️", Toast.LENGTH_SHORT).show() if ((System.currentTimeMillis() % 2) == 0.toLong()) {
Toast.makeText(context, "🍨", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "✈️", Toast.LENGTH_SHORT).show()
}
clickCount = 0
} }
clickCount = 0 },
} topRounded = true
} )
) }
SettingsItem( item {
leadingIconRes = R.drawable.ic_person_4, SettingsItem(
title = stringResource(R.string.pref_developer), leadingIconRes = R.drawable.ic_person_4,
description = stringResource(R.string.developer_name), title = stringResource(R.string.pref_developer),
onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) } description = stringResource(R.string.developer_name),
) onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) },
SettingsItem( )
leadingIcon = GitHubIcon, }
title = stringResource(R.string.pref_view_on_github), item {
description = stringResource(R.string.pref_view_on_github_desc), SettingsItem(
onClick = { uriHandler.openUri(Constants.GITHUB_REPO) } leadingIcon = GitHubIcon,
) title = stringResource(R.string.pref_view_on_github),
SettingsItem( description = stringResource(R.string.pref_view_on_github_desc),
leadingIconRes = R.drawable.ic_balance, onClick = { uriHandler.openUri(Constants.GITHUB_REPO) }
title = stringResource(R.string.pref_licence), )
description = stringResource(R.string.pref_licence_desc), }
onClick = toLicencePage item {
) SettingsItem(
SettingsItem( leadingIconRes = R.drawable.ic_balance,
leadingIconRes = R.drawable.ic_code_blocks, title = stringResource(R.string.pref_licence),
title = stringResource(R.string.pref_developer_options), description = stringResource(R.string.pref_licence_desc),
description = stringResource(R.string.pref_developer_options_desc), onClick = toLicencePage
onClick = toDevPage )
) }
item {
SettingsItem(
leadingIconRes = R.drawable.ic_code_blocks,
title = stringResource(R.string.pref_developer_options),
description = stringResource(R.string.pref_developer_options_desc),
onClick = toDevPage,
bottomRounded = true
)
}
} }
} }
} }

View file

@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -14,7 +13,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.pages.settings.components.licence.LicenceList import cn.super12138.todo.ui.pages.settings.components.licence.LicenceList
import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
@ -24,10 +23,8 @@ fun SettingsAboutLicence(
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() TopAppBarScaffold(
LargeTopAppBarScaffold(
title = stringResource(R.string.pref_licence), title = stringResource(R.string.pref_licence),
scrollBehavior = scrollBehavior,
onBack = onNavigateUp, onBack = onNavigateUp,
modifier = modifier modifier = modifier
) { innerPadding -> ) { innerPadding ->

View file

@ -1,25 +1,20 @@
package cn.super12138.todo.ui.pages.settings package cn.super12138.todo.ui.pages.settings
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.constants.Constants import cn.super12138.todo.constants.Constants
import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.datastore.DataStoreManager
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.pages.settings.components.Settings import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SwitchSettingsItem import cn.super12138.todo.ui.pages.settings.components.SwitchSettingsItem
import cn.super12138.todo.ui.pages.settings.components.appearance.contrast.ContrastPicker import cn.super12138.todo.ui.pages.settings.components.appearance.contrast.ContrastPicker
import cn.super12138.todo.ui.pages.settings.components.appearance.darkmode.DarkModePicker import cn.super12138.todo.ui.pages.settings.components.appearance.darkmode.DarkModePicker
@ -41,46 +36,48 @@ fun SettingsAppearance(
val contrastLevel by DataStoreManager.contrastLevelFlow.collectAsState(initial = Constants.PREF_CONTRAST_LEVEL_DEFAULT) val contrastLevel by DataStoreManager.contrastLevelFlow.collectAsState(initial = Constants.PREF_CONTRAST_LEVEL_DEFAULT)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() TopAppBarScaffold(
LargeTopAppBarScaffold(
title = stringResource(R.string.pref_appearance), title = stringResource(R.string.pref_appearance),
onBack = onNavigateUp, onBack = onNavigateUp,
scrollBehavior = scrollBehavior, modifier = modifier,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { innerPadding -> ) { innerPadding ->
Column( RoundedScreenContainer(Modifier.padding(innerPadding)) {
modifier = Modifier SettingsContainer(Modifier.fillMaxSize()) {
.fillMaxSize() item(key = 1) {
.padding(innerPadding) SwitchSettingsItem(
.padding(horizontal = TodoDefaults.screenHorizontalPadding) checked = dynamicColor,
.verticalScroll(rememberScrollState()) leadingIconRes = R.drawable.ic_wand_stars,
) { title = stringResource(R.string.pref_appearance_dynamic_color),
Settings { description = stringResource(R.string.pref_appearance_dynamic_color_desc),
SwitchSettingsItem( onCheckedChange = { scope.launch { DataStoreManager.setDynamicColor(it) } },
checked = dynamicColor, topRounded = true
leadingIconRes = R.drawable.ic_wand_stars, )
title = stringResource(R.string.pref_appearance_dynamic_color), }
description = stringResource(R.string.pref_appearance_dynamic_color_desc),
onCheckedChange = { scope.launch { DataStoreManager.setDynamicColor(it) } },
)
DarkModePicker( item(key = 2) {
currentDarkMode = { DarkMode.fromId(darkMode) }, DarkModePicker(
onDarkModeChange = { scope.launch { DataStoreManager.setDarkMode(it.id) } } currentDarkMode = { DarkMode.fromId(darkMode) },
) onDarkModeChange = { scope.launch { DataStoreManager.setDarkMode(it.id) } }
)
}
PalettePicker( item(key = 3) {
currentPalette = { PaletteStyle.fromId(paletteStyle) }, PalettePicker(
onPaletteChange = { scope.launch { DataStoreManager.setPaletteStyle(it.id) } }, currentPalette = { PaletteStyle.fromId(paletteStyle) },
isDynamicColor = dynamicColor, onPaletteChange = { scope.launch { DataStoreManager.setPaletteStyle(it.id) } },
isDarkMode = DarkMode.fromId(darkMode), isDynamicColor = dynamicColor,
contrastLevel = ContrastLevel.fromFloat(contrastLevel) isDarkMode = DarkMode.fromId(darkMode),
) contrastLevel = ContrastLevel.fromFloat(contrastLevel)
)
}
ContrastPicker( item(key = 4) {
currentContrast = ContrastLevel.fromFloat(contrastLevel), ContrastPicker(
onContrastChange = { scope.launch { DataStoreManager.setContrastLevel(it.value) } } currentContrast = ContrastLevel.fromFloat(contrastLevel),
) onContrastChange = { scope.launch { DataStoreManager.setContrastLevel(it.value) } },
bottomRounded = true
)
}
} }
} }
} }

View file

@ -4,16 +4,12 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.TopAppBarDefaults
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
@ -22,17 +18,16 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
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.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.activities.MainActivity import cn.super12138.todo.ui.activities.MainActivity
import cn.super12138.todo.ui.components.ConfirmDialog import cn.super12138.todo.ui.components.ConfirmDialog
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.pages.settings.components.Settings import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsCategory import cn.super12138.todo.ui.pages.settings.components.SettingsCategory
import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsItem import cn.super12138.todo.ui.pages.settings.components.SettingsItem
import cn.super12138.todo.ui.viewmodels.MainViewModel import cn.super12138.todo.ui.viewmodels.MainViewModel
import cn.super12138.todo.utils.SystemUtils import cn.super12138.todo.utils.SystemUtils
@ -48,9 +43,7 @@ fun SettingsData(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val context = LocalContext.current val context = LocalContext.current
var showRestoreDialog by rememberSaveable { mutableStateOf(false) } var showRestoreDialog by rememberSaveable { mutableStateOf(false) }
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@ -104,47 +97,49 @@ fun SettingsData(
} }
) )
LargeTopAppBarScaffold( TopAppBarScaffold(
title = stringResource(R.string.pref_data), title = stringResource(R.string.pref_data),
onBack = onNavigateUp, onBack = onNavigateUp,
scrollBehavior = scrollBehavior,
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState) },
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = modifier,
) { innerPadding -> ) { innerPadding ->
Column( RoundedScreenContainer(Modifier.padding(innerPadding)) {
Modifier SettingsContainer(Modifier.fillMaxSize()) {
.fillMaxSize() item {
.padding(innerPadding) SettingsCategory(title = stringResource(R.string.pref_category_data_management), first = true)
.padding(horizontal = TodoDefaults.screenHorizontalPadding) SettingsItem(
.verticalScroll(rememberScrollState()) leadingIconRes = R.drawable.ic_download,
) { title = stringResource(R.string.pref_backup),
SettingsCategory(stringResource(R.string.pref_category_data_management)) description = stringResource(R.string.pref_backup_desc),
Settings { onClick = {
SettingsItem( backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip")
leadingIconRes = R.drawable.ic_download, },
title = stringResource(R.string.pref_backup), topRounded = true
description = stringResource(R.string.pref_backup_desc), )
onClick = { }
backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip") item {
} SettingsItem(
) leadingIconRes = R.drawable.ic_upload,
SettingsItem( title = stringResource(R.string.pref_restore),
leadingIconRes = R.drawable.ic_upload, description = stringResource(R.string.pref_restore_desc),
title = stringResource(R.string.pref_restore), onClick = {
description = stringResource(R.string.pref_restore_desc), restoreLauncher.launch(arrayOf("application/zip"))
onClick = { },
restoreLauncher.launch(arrayOf("application/zip")) bottomRounded = true
} )
) }
}
SettingsCategory(stringResource(R.string.pref_category_category_management)) item {
Settings { SettingsCategory(stringResource(R.string.pref_category_category_management))
SettingsItem( SettingsItem(
leadingIconRes = R.drawable.ic_category, leadingIconRes = R.drawable.ic_category,
title = stringResource(R.string.pref_category_category_management), title = stringResource(R.string.pref_category_category_management),
description = stringResource(R.string.pref_category_management_desc), description = stringResource(R.string.pref_category_management_desc),
onClick = toCategoryManager onClick = toCategoryManager,
) topRounded = true,
bottomRounded = true
)
}
} }
} }
} }

View file

@ -15,7 +15,6 @@ 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
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
@ -26,13 +25,12 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.datastore.DataStoreManager
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.components.TodoFloatingActionButton import cn.super12138.todo.ui.components.TodoFloatingActionButton
import cn.super12138.todo.ui.pages.settings.components.category.CategoryItem import cn.super12138.todo.ui.pages.settings.components.category.CategoryItem
import cn.super12138.todo.ui.pages.settings.components.category.CategoryPromptDialog import cn.super12138.todo.ui.pages.settings.components.category.CategoryPromptDialog
@ -45,7 +43,6 @@ fun SettingsDataCategory(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
// TODO: 本页及其相关组件重组性能检查优化 // TODO: 本页及其相关组件重组性能检查优化
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val listState = rememberLazyListState() val listState = rememberLazyListState()
@ -57,10 +54,9 @@ fun SettingsDataCategory(
val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } } val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
LargeTopAppBarScaffold( TopAppBarScaffold(
title = stringResource(R.string.pref_category_category_management), title = stringResource(R.string.pref_category_category_management),
onBack = onNavigateUp, onBack = onNavigateUp,
scrollBehavior = scrollBehavior,
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState) },
floatingActionButton = { floatingActionButton = {
TodoFloatingActionButton( TodoFloatingActionButton(
@ -73,7 +69,7 @@ fun SettingsDataCategory(
} }
) )
}, },
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = modifier,
) { innerPadding -> ) { innerPadding ->
LazyColumn( LazyColumn(
state = listState, state = listState,

View file

@ -1,20 +1,15 @@
package cn.super12138.todo.ui.pages.settings package cn.super12138.todo.ui.pages.settings
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.pages.settings.components.Settings import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsItem import cn.super12138.todo.ui.pages.settings.components.SettingsItem
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -24,26 +19,22 @@ fun SettingsDeveloperOptions(
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() TopAppBarScaffold(
LargeTopAppBarScaffold(
title = stringResource(R.string.pref_developer_options), title = stringResource(R.string.pref_developer_options),
onBack = onNavigateUp, onBack = onNavigateUp,
scrollBehavior = scrollBehavior, modifier = modifier,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { innerPadding -> ) { innerPadding ->
Column( RoundedScreenContainer(Modifier.padding(innerPadding)) {
modifier = Modifier SettingsContainer(Modifier.fillMaxSize()) {
.fillMaxSize() item {
.padding(innerPadding) SettingsItem(
.padding(horizontal = TodoDefaults.screenHorizontalPadding) leadingIconRes = R.drawable.ic_padding,
.verticalScroll(rememberScrollState()) title = stringResource(R.string.pref_padding),
) { onClick = toPaddingPage,
Settings { topRounded = true,
SettingsItem( bottomRounded = true
leadingIconRes = R.drawable.ic_padding, )
title = stringResource(R.string.pref_padding), }
onClick = toPaddingPage
)
} }
} }
} }

View file

@ -1,13 +1,9 @@
package cn.super12138.todo.ui.pages.settings package cn.super12138.todo.ui.pages.settings
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -17,17 +13,16 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
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 cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.constants.Constants import cn.super12138.todo.constants.Constants
import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.datastore.DataStoreManager
import cn.super12138.todo.logic.model.SortingMethod import cn.super12138.todo.logic.model.SortingMethod
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.pages.settings.components.Settings
import cn.super12138.todo.ui.pages.settings.components.SettingsCategory import cn.super12138.todo.ui.pages.settings.components.SettingsCategory
import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsItem import cn.super12138.todo.ui.pages.settings.components.SettingsItem
import cn.super12138.todo.ui.pages.settings.components.SettingsPlainBox import cn.super12138.todo.ui.pages.settings.components.SettingsPlainBox
import cn.super12138.todo.ui.pages.settings.components.SettingsRadioDialog import cn.super12138.todo.ui.pages.settings.components.SettingsRadioDialog
@ -48,76 +43,79 @@ fun SettingsInterface(
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
var showSortingMethodDialog by rememberSaveable { mutableStateOf(false) } var showSortingMethodDialog by rememberSaveable { mutableStateOf(false) }
LargeTopAppBarScaffold( TopAppBarScaffold(
title = stringResource(R.string.pref_interface_interaction), title = stringResource(R.string.pref_interface_interaction),
onBack = onNavigateUp, onBack = onNavigateUp,
scrollBehavior = scrollBehavior, modifier = modifier,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { innerPadding -> ) { innerPadding ->
Column( RoundedScreenContainer(Modifier.padding(innerPadding)) {
Modifier SettingsContainer(Modifier.fillMaxSize()) {
.fillMaxSize() item {
.padding(innerPadding) SettingsCategory(title = stringResource(R.string.pref_category_todo_list), first = true)
.padding(horizontal = TodoDefaults.screenHorizontalPadding) SwitchSettingsItem(
.verticalScroll(rememberScrollState()) leadingIconRes = R.drawable.ic_checklist,
) { title = stringResource(R.string.pref_show_completed),
SettingsCategory(stringResource(R.string.pref_category_todo_list)) description = stringResource(R.string.pref_show_completed_desc),
Settings { checked = showCompleted,
SwitchSettingsItem( onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } },
leadingIconRes = R.drawable.ic_checklist, topRounded = true
title = stringResource(R.string.pref_show_completed), )
description = stringResource(R.string.pref_show_completed_desc), }
checked = showCompleted, item {
onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } }, SettingsItem(
) leadingIconRes = R.drawable.ic_sort,
SettingsItem( title = stringResource(R.string.pref_sorting_method),
leadingIconRes = R.drawable.ic_sort, description = SortingMethod.fromId(sortingMethod).getDisplayName(context),
title = stringResource(R.string.pref_sorting_method), onClick = { showSortingMethodDialog = true },
description = SortingMethod.fromId(sortingMethod).getDisplayName(context), bottomRounded = true
onClick = { showSortingMethodDialog = true } )
) }
item {
SettingsCategory(stringResource(R.string.pref_category_global))
SwitchSettingsItem(
checked = secureMode,
leadingIconRes = R.drawable.ic_shield,
title = stringResource(R.string.pref_secure_mode),
description = stringResource(R.string.pref_secure_mode_desc),
onCheckedChange = { scope.launch { DataStoreManager.setSecureMode(it) } },
topRounded = true
)
}
item {
SwitchSettingsItem(
checked = hapticFeedback,
leadingIconRes = R.drawable.ic_touch_long,
title = stringResource(R.string.pref_haptic_feedback),
description = stringResource(R.string.pref_haptic_feedback_desc),
onCheckedChange = { scope.launch { DataStoreManager.setHapticFeedback(it) } },
bottomRounded = true
)
SettingsPlainBox(stringResource(R.string.pref_haptic_feedback_more_info))
}
} }
SettingsCategory(stringResource(R.string.pref_category_global))
Settings {
SwitchSettingsItem(
checked = secureMode,
leadingIconRes = R.drawable.ic_shield,
title = stringResource(R.string.pref_secure_mode),
description = stringResource(R.string.pref_secure_mode_desc),
onCheckedChange = { scope.launch { DataStoreManager.setSecureMode(it) } }
)
SwitchSettingsItem(
checked = hapticFeedback,
leadingIconRes = R.drawable.ic_touch_long,
title = stringResource(R.string.pref_haptic_feedback),
description = stringResource(R.string.pref_haptic_feedback_desc),
onCheckedChange = { scope.launch { DataStoreManager.setHapticFeedback(it) } }
)
}
SettingsPlainBox(stringResource(R.string.pref_haptic_feedback_more_info))
} }
} val sortingList = remember {
SortingMethod.entries.map {
val sortingList = remember { SettingsRadioOptions(
SortingMethod.entries.map { id = it.id,
SettingsRadioOptions( text = it.getDisplayName(context)
id = it.id, )
text = it.getDisplayName(context) }
)
} }
SettingsRadioDialog(
visible = showSortingMethodDialog,
title = stringResource(R.string.pref_sorting_method),
currentOptions = SettingsRadioOptions(
id = sortingMethod,
text = SortingMethod.fromId(sortingMethod).getDisplayName(context)
),
options = sortingList,
onSelect = { scope.launch { DataStoreManager.setSortingMethod(it) } },
onDismiss = { showSortingMethodDialog = false }
)
} }
SettingsRadioDialog(
visible = showSortingMethodDialog,
title = stringResource(R.string.pref_sorting_method),
currentOptions = SettingsRadioOptions(
id = sortingMethod,
text = SortingMethod.fromId(sortingMethod).getDisplayName(context)
),
options = sortingList,
onSelect = { scope.launch { DataStoreManager.setSortingMethod(it) } },
onDismiss = { showSortingMethodDialog = false }
)
} }

View file

@ -1,21 +1,16 @@
package cn.super12138.todo.ui.pages.settings package cn.super12138.todo.ui.pages.settings
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.pages.settings.components.Settings import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsItem import cn.super12138.todo.ui.pages.settings.components.SettingsItem
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@ -28,46 +23,50 @@ fun SettingsMain(
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() TopAppBarScaffold(
LargeTopAppBarScaffold(
title = stringResource(R.string.page_settings), title = stringResource(R.string.page_settings),
scrollBehavior = scrollBehavior,
onBack = onNavigateUp, onBack = onNavigateUp,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection) modifier = modifier
) { innerPadding -> ) { innerPadding ->
Column( RoundedScreenContainer(Modifier.padding(innerPadding)) {
Modifier SettingsContainer(Modifier.fillMaxSize()) {
.fillMaxSize() item {
.padding(innerPadding) SettingsItem(
.padding(horizontal = TodoDefaults.screenHorizontalPadding) leadingIconRes = R.drawable.ic_palette,
.verticalScroll(rememberScrollState()) title = stringResource(R.string.pref_appearance),
) { description = stringResource(R.string.pref_appearance_desc),
Settings { onClick = toAppearancePage,
SettingsItem( topRounded = true
leadingIconRes = R.drawable.ic_palette, )
title = stringResource(R.string.pref_appearance), }
description = stringResource(R.string.pref_appearance_desc),
onClick = toAppearancePage
)
SettingsItem(
leadingIconRes = R.drawable.ic_view_comfy,
title = stringResource(R.string.pref_interface_interaction),
description = stringResource(R.string.pref_interface_interaction_desc),
onClick = toInterfacePage
)
SettingsItem(
leadingIconRes = R.drawable.ic_dns,
title = stringResource(R.string.pref_data),
description = stringResource(R.string.pref_data_desc),
onClick = toDataPage
)
SettingsItem(
leadingIconRes = R.drawable.ic_info,
title = stringResource(R.string.pref_about),
description = stringResource(R.string.pref_about_desc),
onClick = toAboutPage
)
item {
SettingsItem(
leadingIconRes = R.drawable.ic_view_comfy,
title = stringResource(R.string.pref_interface_interaction),
description = stringResource(R.string.pref_interface_interaction_desc),
onClick = toInterfacePage
)
}
item {
SettingsItem(
leadingIconRes = R.drawable.ic_dns,
title = stringResource(R.string.pref_data),
description = stringResource(R.string.pref_data_desc),
onClick = toDataPage
)
}
item {
SettingsItem(
leadingIconRes = R.drawable.ic_info,
title = stringResource(R.string.pref_about),
description = stringResource(R.string.pref_about_desc),
onClick = toAboutPage,
bottomRounded = true
)
}
} }
} }
} }

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -20,19 +21,15 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
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 androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.drawFadedEdge
import cn.super12138.todo.utils.getPartialRoundedShape
@Composable @Composable
fun RowSettingsItem( fun RowSettingsItem(
@ -41,13 +38,14 @@ fun RowSettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.small, background: Color = TodoDefaults.ContainerColor,
background: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top, verticalAlignment: Alignment.Vertical = Alignment.Top,
scrollState: ScrollState = rememberScrollState(), scrollState: ScrollState = rememberScrollState(),
fadedEdgeWidth: Dp, fadedEdgeWidth: Dp,
maskColor: Color = MaterialTheme.colorScheme.background, maskColor: Color = TodoDefaults.ContainerColor,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
MoreContentSettingsItem( MoreContentSettingsItem(
@ -55,8 +53,9 @@ fun RowSettingsItem(
title = title, title = title,
description = description, description = description,
trailingContent = trailingContent, trailingContent = trailingContent,
shape = shape,
background = background, background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
modifier = modifier modifier = modifier
) { ) {
Row( Row(
@ -94,12 +93,13 @@ fun LazyRowSettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.small, background: Color = TodoDefaults.ContainerColor,
background: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top, verticalAlignment: Alignment.Vertical = Alignment.Top,
fadedEdgeWidth: Dp, fadedEdgeWidth: Dp,
maskColor: Color = MaterialTheme.colorScheme.background, maskColor: Color = TodoDefaults.ContainerColor,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
content: LazyListScope.() -> Unit content: LazyListScope.() -> Unit
) { ) {
MoreContentSettingsItem( MoreContentSettingsItem(
@ -107,7 +107,8 @@ fun LazyRowSettingsItem(
title = title, title = title,
description = description, description = description,
trailingContent = trailingContent, trailingContent = trailingContent,
shape = shape, topRounded = topRounded,
bottomRounded = bottomRounded,
background = background, background = background,
modifier = modifier modifier = modifier
) { ) {
@ -145,15 +146,18 @@ fun MoreContentSettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.small, background: Color = TodoDefaults.ContainerColor,
background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, shape: CornerBasedShape = TodoDefaults.SettingsItemDefaultShape,
roundedShape: CornerBasedShape = TodoDefaults.SettingsItemRoundedShape,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Column( Column(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
.clip(shape) .clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape))
.background(background) .background(background)
.padding( .padding(
horizontal = TodoDefaults.settingsItemHorizontalPadding, horizontal = TodoDefaults.settingsItemHorizontalPadding,
@ -195,22 +199,3 @@ fun MoreContentSettingsItem(
content() content()
} }
} }
fun ContentDrawScope.drawFadedEdge(
edgeWidth: Dp,
maskColor: Color,
leftEdge: Boolean
) {
val edgeWidthPx = edgeWidth.toPx()
drawRect(
topLeft = Offset(if (leftEdge) 0f else size.width - edgeWidthPx, 0f),
size = Size(edgeWidthPx, size.height),
brush =
Brush.horizontalGradient(
colors = listOf(Color.Transparent, maskColor),
startX = if (leftEdge) 0f else size.width,
endX = if (leftEdge) edgeWidthPx else size.width - edgeWidthPx
),
blendMode = BlendMode.DstIn
)
}

View file

@ -8,20 +8,23 @@ 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.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
@Composable @Composable
fun SettingsCategory( fun SettingsCategory(
title: String, title: String,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
first: Boolean = false,
) { ) {
Box( Box(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
top = TodoDefaults.settingsItemVerticalPadding / 2, top = if (first) 0.dp else TodoDefaults.settingsItemVerticalPadding,
start = TodoDefaults.settingsItemHorizontalPadding, start = TodoDefaults.settingsItemHorizontalPadding / 2,
end = TodoDefaults.settingsItemHorizontalPadding, end = TodoDefaults.settingsItemHorizontalPadding / 2,
bottom = TodoDefaults.settingsItemVerticalPadding / 2
) )
) { ) {
Text( Text(

View file

@ -1,28 +1,35 @@
package cn.super12138.todo.ui.pages.settings.components package cn.super12138.todo.ui.pages.settings.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun Settings( fun SettingsContainer(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit, state: LazyListState = rememberLazyListState(),
content: LazyListScope.() -> Unit,
) { ) {
Column( LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp), verticalArrangement = Arrangement.spacedBy(TodoDefaults.settingsItemPadding),
state = state,
modifier = modifier modifier = modifier
.padding(vertical = TodoDefaults.screenVerticalPadding) ) {
.clip(MaterialTheme.shapes.extraLarge), item {
content = content Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding))
) }
content()
item {
Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding))
}
}
} }

View file

@ -76,7 +76,7 @@ fun RadioItem(
modifier modifier
.fillMaxWidth() .fillMaxWidth()
.height(56.dp) .height(56.dp)
.clip(MaterialTheme.shapes.small) .clip(TodoDefaults.SettingsItemDefaultShape)
.background(MaterialTheme.colorScheme.surfaceContainerHighest) .background(MaterialTheme.colorScheme.surfaceContainerHighest)
.selectable( .selectable(
selected = selected, selected = selected,

View file

@ -8,6 +8,7 @@ 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.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CornerBasedShape
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
@ -17,7 +18,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
@ -26,6 +26,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.VibrationUtils import cn.super12138.todo.utils.VibrationUtils
import cn.super12138.todo.utils.getPartialRoundedShape
@Composable @Composable
fun SettingsItem( fun SettingsItem(
@ -33,6 +34,8 @@ fun SettingsItem(
@DrawableRes leadingIconRes: Int, @DrawableRes leadingIconRes: Int,
title: String, title: String,
description: String? = null, description: String? = null,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
@ -41,6 +44,8 @@ fun SettingsItem(
title = title, title = title,
description = description, description = description,
trailingContent = null, trailingContent = null,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick, enableClick = enableClick,
onClick = onClick, onClick = onClick,
modifier = modifier modifier = modifier
@ -53,6 +58,8 @@ fun SettingsItem(
leadingIcon: ImageVector? = null, leadingIcon: ImageVector? = null,
title: String, title: String,
description: String? = null, description: String? = null,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
@ -61,6 +68,8 @@ fun SettingsItem(
title = title, title = title,
description = description, description = description,
trailingContent = null, trailingContent = null,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick, enableClick = enableClick,
onClick = onClick, onClick = onClick,
modifier = modifier modifier = modifier
@ -74,7 +83,9 @@ fun SettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, background: Color = TodoDefaults.ContainerColor,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
@ -93,6 +104,8 @@ fun SettingsItem(
description = description, description = description,
trailingContent = trailingContent, trailingContent = trailingContent,
background = background, background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick, enableClick = enableClick,
onClick = onClick, onClick = onClick,
modifier = modifier modifier = modifier
@ -106,7 +119,9 @@ fun SettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, background: Color = TodoDefaults.ContainerColor,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
@ -125,6 +140,8 @@ fun SettingsItem(
description = description, description = description,
trailingContent = trailingContent, trailingContent = trailingContent,
background = background, background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick, enableClick = enableClick,
onClick = onClick, onClick = onClick,
modifier = modifier modifier = modifier
@ -139,8 +156,11 @@ fun SettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.small, background: Color = TodoDefaults.ContainerColor,
background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, shape: CornerBasedShape = TodoDefaults.SettingsItemDefaultShape,
roundedShape: CornerBasedShape = TodoDefaults.SettingsItemRoundedShape,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
) { ) {
@ -149,7 +169,7 @@ fun SettingsItem(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
.clip(shape) .clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape))
.clickable( .clickable(
enabled = enableClick, enabled = enableClick,
onClick = { onClick = {

View file

@ -30,9 +30,8 @@ fun SettingsPlainBox(
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
.padding( .padding(
start = TodoDefaults.settingsItemHorizontalPadding, vertical = TodoDefaults.settingsItemVerticalPadding,
end = TodoDefaults.settingsItemHorizontalPadding, horizontal = TodoDefaults.settingsItemHorizontalPadding / 2
bottom = TodoDefaults.settingsItemVerticalPadding
), ),
) { ) {
Icon( Icon(

View file

@ -17,10 +17,11 @@ fun SwitchSettingsItem(
@DrawableRes leadingIconRes: Int, @DrawableRes leadingIconRes: Int,
title: String, title: String,
description: String? = null, description: String? = null,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
checked: Boolean, checked: Boolean,
onCheckedChange: (Boolean) -> Unit onCheckedChange: (Boolean) -> Unit
) { ) {
val view = LocalView.current
SettingsItem( SettingsItem(
leadingIcon = painterResource(leadingIconRes), leadingIcon = painterResource(leadingIconRes),
title = title, title = title,
@ -28,14 +29,13 @@ fun SwitchSettingsItem(
trailingContent = { trailingContent = {
Switch( Switch(
checked = checked, checked = checked,
onCheckedChange = { onCheckedChange = null,
VibrationUtils.performHapticFeedback(view)
onCheckedChange(it)
},
modifier = Modifier.padding(start = TodoDefaults.settingsItemHorizontalPadding / 2) modifier = Modifier.padding(start = TodoDefaults.settingsItemHorizontalPadding / 2)
) )
}, },
onClick = { onCheckedChange(!checked) }, onClick = { onCheckedChange(!checked) },
topRounded = topRounded,
bottomRounded = bottomRounded,
modifier = modifier, modifier = modifier,
) )
} }

View file

@ -35,15 +35,19 @@ import cn.super12138.todo.utils.VibrationUtils
@Composable @Composable
fun ContrastPicker( fun ContrastPicker(
modifier: Modifier = Modifier,
currentContrast: ContrastLevel, currentContrast: ContrastLevel,
onContrastChange: (ContrastLevel) -> Unit, onContrastChange: (ContrastLevel) -> Unit,
modifier: Modifier = Modifier topRounded: Boolean = false,
bottomRounded: Boolean = false
) { ) {
val view = LocalView.current val view = LocalView.current
val context = LocalContext.current val context = LocalContext.current
MoreContentSettingsItem( MoreContentSettingsItem(
title = stringResource(R.string.pref_contrast_level), title = stringResource(R.string.pref_contrast_level),
description = stringResource(R.string.pref_contrast_level_desc), description = stringResource(R.string.pref_contrast_level_desc),
topRounded = topRounded,
bottomRounded = bottomRounded,
modifier = modifier modifier = modifier
) { ) {
val contrastLevelName = val contrastLevelName =

View file

@ -13,14 +13,17 @@ import androidx.compose.ui.platform.LocalContext
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 cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.pages.settings.components.LazyRowSettingsItem import cn.super12138.todo.ui.pages.settings.components.LazyRowSettingsItem
import cn.super12138.todo.ui.theme.DarkMode import cn.super12138.todo.ui.theme.DarkMode
@Composable @Composable
fun DarkModePicker( fun DarkModePicker(
modifier: Modifier = Modifier,
currentDarkMode: () -> DarkMode, currentDarkMode: () -> DarkMode,
onDarkModeChange: (darkMode: DarkMode) -> Unit, onDarkModeChange: (darkMode: DarkMode) -> Unit,
modifier: Modifier = Modifier topRounded: Boolean = false,
bottomRounded: Boolean = false
) { ) {
val context = LocalContext.current val context = LocalContext.current
val isInDarkTheme = isSystemInDarkTheme() val isInDarkTheme = isSystemInDarkTheme()
@ -31,7 +34,9 @@ fun DarkModePicker(
title = stringResource(R.string.pref_dark_mode), title = stringResource(R.string.pref_dark_mode),
description = stringResource(R.string.pref_dark_mode_desc), description = stringResource(R.string.pref_dark_mode_desc),
horizontalArrangement = Arrangement.spacedBy(5.dp), horizontalArrangement = Arrangement.spacedBy(5.dp),
fadedEdgeWidth = 8.dp, fadedEdgeWidth = TodoDefaults.fadedEdgeWidth,
topRounded = topRounded,
bottomRounded = bottomRounded,
modifier = modifier modifier = modifier
) { ) {
items(items = darkModeList, key = { it.id }) { items(items = darkModeList, key = { it.id }) {

View file

@ -11,6 +11,7 @@ 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 cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.pages.settings.components.LazyRowSettingsItem import cn.super12138.todo.ui.pages.settings.components.LazyRowSettingsItem
import cn.super12138.todo.ui.theme.ContrastLevel import cn.super12138.todo.ui.theme.ContrastLevel
import cn.super12138.todo.ui.theme.DarkMode import cn.super12138.todo.ui.theme.DarkMode
@ -18,12 +19,14 @@ import cn.super12138.todo.ui.theme.PaletteStyle
@Composable @Composable
fun PalettePicker( fun PalettePicker(
modifier: Modifier = Modifier,
currentPalette: () -> PaletteStyle, currentPalette: () -> PaletteStyle,
onPaletteChange: (paletteStyle: PaletteStyle) -> Unit, onPaletteChange: (paletteStyle: PaletteStyle) -> Unit,
isDynamicColor: Boolean, isDynamicColor: Boolean,
isDarkMode: DarkMode, isDarkMode: DarkMode,
contrastLevel: ContrastLevel, contrastLevel: ContrastLevel,
modifier: Modifier = Modifier topRounded: Boolean = false,
bottomRounded: Boolean = false
) { ) {
val paletteOptions = remember { PaletteStyle.entries.toList() } val paletteOptions = remember { PaletteStyle.entries.toList() }
@ -31,7 +34,9 @@ fun PalettePicker(
title = stringResource(R.string.pref_palette_style), title = stringResource(R.string.pref_palette_style),
description = stringResource(R.string.pref_palette_style_desc), description = stringResource(R.string.pref_palette_style_desc),
horizontalArrangement = Arrangement.spacedBy(5.dp), horizontalArrangement = Arrangement.spacedBy(5.dp),
fadedEdgeWidth = 8.dp, fadedEdgeWidth = TodoDefaults.fadedEdgeWidth,
topRounded = topRounded,
bottomRounded = bottomRounded,
modifier = modifier modifier = modifier
) { ) {
items(items = paletteOptions, key = { it.id }) { items(items = paletteOptions, key = { it.id }) {

View file

@ -1,9 +1,16 @@
package cn.super12138.todo.utils package cn.super12138.todo.utils
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.unit.Dp
import cn.super12138.todo.logic.model.Priority import cn.super12138.todo.logic.model.Priority
@Composable @Composable
@ -15,4 +22,37 @@ fun Priority.containerColor(): Color =
Priority.Default -> MaterialTheme.colorScheme.secondary Priority.Default -> MaterialTheme.colorScheme.secondary
Priority.Important -> MaterialTheme.colorScheme.tertiary Priority.Important -> MaterialTheme.colorScheme.tertiary
Priority.Urgent -> MaterialTheme.colorScheme.error Priority.Urgent -> MaterialTheme.colorScheme.error
} }
@Composable
fun CornerBasedShape.getPartialRoundedShape(
topRounded: Boolean,
bottomRounded: Boolean,
roundedShape: CornerBasedShape
): CornerBasedShape =
this.copy(
topStart = if (topRounded) roundedShape.topStart else this.topStart,
topEnd = if (topRounded) roundedShape.topEnd else this.topEnd,
bottomEnd = if (bottomRounded) roundedShape.bottomEnd else this.bottomEnd,
bottomStart = if (bottomRounded) roundedShape.bottomStart else this.bottomStart,
)
fun ContentDrawScope.drawFadedEdge(
edgeWidth: Dp,
maskColor: Color,
leftEdge: Boolean
) {
val edgeWidthPx = edgeWidth.toPx()
drawRect(
topLeft = Offset(if (leftEdge) 0f else size.width - edgeWidthPx, 0f),
size = Size(edgeWidthPx, size.height),
brush =
Brush.horizontalGradient(
colors = listOf(Color.Transparent, maskColor),
startX = if (leftEdge) 0f else size.width,
endX = if (leftEdge) edgeWidthPx else size.width - edgeWidthPx
),
blendMode = BlendMode.DstIn
)
}