diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7acc6f2..6d4e2d2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -38,7 +38,7 @@ android { applicationId = "cn.super12138.todo" minSdk = 24 targetSdk = 36 - versionCode = 923 + versionCode = 940 versionName = "2.3.3" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt b/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt index f5f09f4..c3b4264 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/TodoDefaults.kt @@ -1,5 +1,11 @@ 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 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 + + 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 } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt b/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt index 11724d2..0bd774e 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/activities/CrashActivity.kt @@ -73,7 +73,7 @@ class CrashActivity : ComponentActivity() { ) { CrashPage( crashLog = crashLogs ?: stringResource(R.string.tip_no_crash_logs), - exitApp = { finishAffinity() }, + exitApp = ::finishAffinity, modifier = Modifier.fillMaxSize() ) } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt b/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt index a31cf2a..c2d7f72 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt @@ -8,7 +8,6 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -19,6 +18,7 @@ import androidx.core.view.WindowCompat import androidx.lifecycle.viewmodel.compose.viewModel import cn.super12138.todo.constants.Constants 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.navigation.TodoNavigation import cn.super12138.todo.ui.theme.DarkMode @@ -83,7 +83,7 @@ class MainActivity : ComponentActivity() { contrastLevel = contrastLevel.toDouble(), dynamicColor = dynamicColor ) { - Surface(color = MaterialTheme.colorScheme.background) { + Surface(color = TodoDefaults.BackgroundColor) { TodoNavigation( viewModel = mainViewModel, modifier = Modifier.fillMaxSize() diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt index 158fe2a..895cbf5 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt @@ -4,26 +4,28 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.FabPosition import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonDefaults -import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.ScaffoldDefaults +import androidx.compose.material3.Surface 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.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import cn.super12138.todo.R +import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.utils.VibrationUtils /** @@ -31,16 +33,14 @@ import cn.super12138.todo.utils.VibrationUtils * * 内容默认由 Box 容器包裹;实际使用时推荐配合 Column 或 Row * * @param title 标题文本 - * @param scrollBehavior 滚动行为,用于支持页面内容滚动时标题栏的压缩效果 * @param onBack 当返回按钮被按下时的操作 * @param contentWindowInsets 内容边距,通常用于将内容和系统状态栏等隔开;可以使用 `WindowInsets.safeContent` */ @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable -fun LargeTopAppBarScaffold( +fun TopAppBarScaffold( modifier: Modifier = Modifier, title: String, - scrollBehavior: TopAppBarScrollBehavior, onBack: () -> Unit, snackbarHost: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {}, @@ -49,7 +49,7 @@ fun LargeTopAppBarScaffold( content: @Composable (PaddingValues) -> Unit ) { val view = LocalView.current - LargeTopAppBarScaffold( + TopAppBarScaffold( title = { Text( text = title, @@ -59,7 +59,7 @@ fun LargeTopAppBarScaffold( }, navigationIcon = { FilledIconButton( - colors = IconButtonDefaults.filledIconButtonColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh), + colors = IconButtonDefaults.filledIconButtonColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHighest), shapes = IconButtonDefaults.shapes(), onClick = { VibrationUtils.performHapticFeedback(view) @@ -72,25 +72,24 @@ fun LargeTopAppBarScaffold( ) } }, - scrollBehavior = scrollBehavior, snackbarHost = snackbarHost, floatingActionButton = floatingActionButton, floatingActionButtonPosition = floatingActionButtonPosition, contentWindowInsets = contentWindowInsets, - modifier = modifier + modifier = modifier, ) { innerPadding -> + //TODO: 应用圆角效果 Box { content(innerPadding) } } } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LargeTopAppBarScaffold( +fun TopAppBarScaffold( modifier: Modifier = Modifier, title: @Composable () -> Unit = {}, navigationIcon: @Composable () -> Unit = {}, actions: @Composable RowScope.() -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior, snackbarHost: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {}, floatingActionButtonPosition: FabPosition = FabPosition.End, @@ -98,20 +97,22 @@ fun LargeTopAppBarScaffold( content: @Composable (PaddingValues) -> Unit ) { Scaffold( - modifier = modifier - .nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, topBar = { - LargeTopAppBar( + TopAppBar( title = title, navigationIcon = navigationIcon, actions = actions, - scrollBehavior = scrollBehavior + colors = TopAppBarDefaults.topAppBarColors( + containerColor = TodoDefaults.BackgroundColor, + ) ) }, snackbarHost = snackbarHost, floatingActionButton = floatingActionButton, floatingActionButtonPosition = floatingActionButtonPosition, - contentWindowInsets = contentWindowInsets + contentWindowInsets = contentWindowInsets, + containerColor = TodoDefaults.BackgroundColor, ) { innerPadding -> Box { content(innerPadding) } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/ScreenContainer.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/ScreenContainer.kt new file mode 100644 index 0000000..68a8f9e --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/ScreenContainer.kt @@ -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 + ) +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt index f2904e2..36e453c 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt @@ -44,7 +44,7 @@ import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.components.ChipItem 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.pages.editor.components.TodoCategoryChip import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField @@ -69,7 +69,7 @@ fun TodoEditorPage( // TODO: 本页及其相关组件重组性能检查优化 val view = LocalView.current val animatedVisibilityScope = LocalNavAnimatedContentScope.current - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() + TopAppBarDefaults.exitUntilCollapsedScrollBehavior() val uiState = rememberEditorState(initialTodo = toDo) @@ -112,9 +112,8 @@ fun TodoEditorPage( BackHandler(onBack = ::checkModifiedBeforeBack) - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task), - scrollBehavior = scrollBehavior, floatingActionButton = { with(sharedTransitionScope) { Row( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt index 898ba75..717c55d 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt @@ -1,15 +1,10 @@ package cn.super12138.todo.ui.pages.settings 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.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -18,17 +13,15 @@ import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import cn.super12138.todo.R import cn.super12138.todo.constants.Constants -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.components.RoundedScreenContainer 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.utils.SystemUtils import kotlinx.coroutines.delay @@ -42,80 +35,82 @@ fun SettingsAbout( onNavigateUp: () -> Unit, modifier: Modifier = Modifier ) { - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_about), onBack = onNavigateUp, - scrollBehavior = scrollBehavior, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, ) { innerPadding -> val context = LocalContext.current val uriHandler = LocalUriHandler.current + var clickCount by remember { mutableIntStateOf(0) } + var lastClickTime by remember { mutableLongStateOf(0L) } - Column( - verticalArrangement = Arrangement.spacedBy(2.dp), - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .verticalScroll(rememberScrollState()) - ) { - Settings { - var clickCount by remember { mutableIntStateOf(0) } - var lastClickTime by remember { mutableLongStateOf(0L) } + LaunchedEffect(clickCount) { + if (clickCount > 0) { + lastClickTime = System.currentTimeMillis() + val currentClickTime = lastClickTime + delay(300L) - LaunchedEffect(clickCount) { - if (clickCount > 0) { - lastClickTime = System.currentTimeMillis() - val currentClickTime = lastClickTime - delay(300L) - - if (currentClickTime == lastClickTime) { - clickCount = 0 - } - } + if (currentClickTime == lastClickTime) { + clickCount = 0 } + } + } - SettingsItem( - leadingIconRes = R.drawable.ic_numbers, - title = stringResource(R.string.pref_app_version), - description = SystemUtils.getAppVersion(context), - onClick = { - clickCount++ - if (clickCount == 5) { - if ((System.currentTimeMillis() % 2) == 0.toLong()) { - Toast.makeText(context, "🍨", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(context, "✈️", Toast.LENGTH_SHORT).show() + RoundedScreenContainer(Modifier.padding(innerPadding)) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsItem( + leadingIconRes = R.drawable.ic_numbers, + title = stringResource(R.string.pref_app_version), + description = SystemUtils.getAppVersion(context), + onClick = { + clickCount++ + if (clickCount == 5) { + 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 - } - } - ) - SettingsItem( - leadingIconRes = R.drawable.ic_person_4, - title = stringResource(R.string.pref_developer), - description = stringResource(R.string.developer_name), - onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) } - ) - SettingsItem( - leadingIcon = GitHubIcon, - title = stringResource(R.string.pref_view_on_github), - description = stringResource(R.string.pref_view_on_github_desc), - onClick = { uriHandler.openUri(Constants.GITHUB_REPO) } - ) - SettingsItem( - leadingIconRes = R.drawable.ic_balance, - title = stringResource(R.string.pref_licence), - description = stringResource(R.string.pref_licence_desc), - onClick = toLicencePage - ) - SettingsItem( - leadingIconRes = R.drawable.ic_code_blocks, - title = stringResource(R.string.pref_developer_options), - description = stringResource(R.string.pref_developer_options_desc), - onClick = toDevPage - ) + }, + topRounded = true + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_person_4, + title = stringResource(R.string.pref_developer), + description = stringResource(R.string.developer_name), + onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) }, + ) + } + item { + SettingsItem( + leadingIcon = GitHubIcon, + title = stringResource(R.string.pref_view_on_github), + description = stringResource(R.string.pref_view_on_github_desc), + onClick = { uriHandler.openUri(Constants.GITHUB_REPO) } + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_balance, + title = stringResource(R.string.pref_licence), + description = stringResource(R.string.pref_licence_desc), + onClick = toLicencePage + ) + } + 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 + ) + } } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt index 4a08ad9..402a307 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -14,7 +13,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import cn.super12138.todo.R 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 com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries @@ -24,10 +23,8 @@ fun SettingsAboutLicence( onNavigateUp: () -> Unit, modifier: Modifier = Modifier ) { - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_licence), - scrollBehavior = scrollBehavior, onBack = onNavigateUp, modifier = modifier ) { innerPadding -> diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt index f881f59..afdc329 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt @@ -1,25 +1,20 @@ package cn.super12138.todo.ui.pages.settings -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize 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.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.datastore.DataStoreManager -import cn.super12138.todo.ui.TodoDefaults -import cn.super12138.todo.ui.components.LargeTopAppBarScaffold -import cn.super12138.todo.ui.pages.settings.components.Settings +import cn.super12138.todo.ui.components.TopAppBarScaffold +import cn.super12138.todo.ui.components.RoundedScreenContainer +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.appearance.contrast.ContrastPicker 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 scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_appearance), onBack = onNavigateUp, - scrollBehavior = scrollBehavior, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, ) { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .verticalScroll(rememberScrollState()) - ) { - Settings { - SwitchSettingsItem( - checked = dynamicColor, - 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) } }, - ) + RoundedScreenContainer(Modifier.padding(innerPadding)) { + SettingsContainer(Modifier.fillMaxSize()) { + item(key = 1) { + SwitchSettingsItem( + checked = dynamicColor, + 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) } }, + topRounded = true + ) + } - DarkModePicker( - currentDarkMode = { DarkMode.fromId(darkMode) }, - onDarkModeChange = { scope.launch { DataStoreManager.setDarkMode(it.id) } } - ) + item(key = 2) { + DarkModePicker( + currentDarkMode = { DarkMode.fromId(darkMode) }, + onDarkModeChange = { scope.launch { DataStoreManager.setDarkMode(it.id) } } + ) + } - PalettePicker( - currentPalette = { PaletteStyle.fromId(paletteStyle) }, - onPaletteChange = { scope.launch { DataStoreManager.setPaletteStyle(it.id) } }, - isDynamicColor = dynamicColor, - isDarkMode = DarkMode.fromId(darkMode), - contrastLevel = ContrastLevel.fromFloat(contrastLevel) - ) + item(key = 3) { + PalettePicker( + currentPalette = { PaletteStyle.fromId(paletteStyle) }, + onPaletteChange = { scope.launch { DataStoreManager.setPaletteStyle(it.id) } }, + isDynamicColor = dynamicColor, + isDarkMode = DarkMode.fromId(darkMode), + contrastLevel = ContrastLevel.fromFloat(contrastLevel) + ) + } - ContrastPicker( - currentContrast = ContrastLevel.fromFloat(contrastLevel), - onContrastChange = { scope.launch { DataStoreManager.setContrastLevel(it.value) } } - ) + item(key = 4) { + ContrastPicker( + currentContrast = ContrastLevel.fromFloat(contrastLevel), + onContrastChange = { scope.launch { DataStoreManager.setContrastLevel(it.value) } }, + bottomRounded = true + ) + } } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt index 227c668..24c0d06 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsData.kt @@ -4,16 +4,12 @@ import android.content.Context import android.content.Intent import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize 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.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -22,17 +18,16 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.window.DialogProperties import cn.super12138.todo.R -import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.activities.MainActivity import cn.super12138.todo.ui.components.ConfirmDialog -import cn.super12138.todo.ui.components.LargeTopAppBarScaffold -import cn.super12138.todo.ui.pages.settings.components.Settings +import cn.super12138.todo.ui.components.TopAppBarScaffold +import cn.super12138.todo.ui.components.RoundedScreenContainer 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.viewmodels.MainViewModel import cn.super12138.todo.utils.SystemUtils @@ -48,9 +43,7 @@ fun SettingsData( modifier: Modifier = Modifier ) { val context = LocalContext.current - var showRestoreDialog by rememberSaveable { mutableStateOf(false) } - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -104,47 +97,49 @@ fun SettingsData( } ) - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_data), onBack = onNavigateUp, - scrollBehavior = scrollBehavior, snackbarHost = { SnackbarHost(snackbarHostState) }, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, ) { innerPadding -> - Column( - Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .verticalScroll(rememberScrollState()) - ) { - SettingsCategory(stringResource(R.string.pref_category_data_management)) - Settings { - SettingsItem( - leadingIconRes = R.drawable.ic_download, - title = stringResource(R.string.pref_backup), - description = stringResource(R.string.pref_backup_desc), - onClick = { - backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip") - } - ) - SettingsItem( - leadingIconRes = R.drawable.ic_upload, - title = stringResource(R.string.pref_restore), - description = stringResource(R.string.pref_restore_desc), - onClick = { - restoreLauncher.launch(arrayOf("application/zip")) - } - ) - } - SettingsCategory(stringResource(R.string.pref_category_category_management)) - Settings { - SettingsItem( - leadingIconRes = R.drawable.ic_category, - title = stringResource(R.string.pref_category_category_management), - description = stringResource(R.string.pref_category_management_desc), - onClick = toCategoryManager - ) + RoundedScreenContainer(Modifier.padding(innerPadding)) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsCategory(title = stringResource(R.string.pref_category_data_management), first = true) + SettingsItem( + leadingIconRes = R.drawable.ic_download, + title = stringResource(R.string.pref_backup), + description = stringResource(R.string.pref_backup_desc), + onClick = { + backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip") + }, + topRounded = true + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_upload, + title = stringResource(R.string.pref_restore), + description = stringResource(R.string.pref_restore_desc), + onClick = { + restoreLauncher.launch(arrayOf("application/zip")) + }, + bottomRounded = true + ) + } + + item { + SettingsCategory(stringResource(R.string.pref_category_category_management)) + SettingsItem( + leadingIconRes = R.drawable.ic_category, + title = stringResource(R.string.pref_category_category_management), + description = stringResource(R.string.pref_category_management_desc), + onClick = toCategoryManager, + topRounded = true, + bottomRounded = true + ) + } } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt index 38f0e35..ab3db90 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt @@ -15,7 +15,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf @@ -26,13 +25,12 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntOffset import cn.super12138.todo.R 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.pages.settings.components.category.CategoryItem import cn.super12138.todo.ui.pages.settings.components.category.CategoryPromptDialog @@ -45,7 +43,6 @@ fun SettingsDataCategory( modifier: Modifier = Modifier ) { // TODO: 本页及其相关组件重组性能检查优化 - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() val listState = rememberLazyListState() @@ -57,10 +54,9 @@ fun SettingsDataCategory( val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } } - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_category_category_management), onBack = onNavigateUp, - scrollBehavior = scrollBehavior, snackbarHost = { SnackbarHost(snackbarHostState) }, floatingActionButton = { TodoFloatingActionButton( @@ -73,7 +69,7 @@ fun SettingsDataCategory( } ) }, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, ) { innerPadding -> LazyColumn( state = listState, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt index 76507d2..9e539c9 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDeveloperOptions.kt @@ -1,20 +1,15 @@ package cn.super12138.todo.ui.pages.settings -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize 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.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import cn.super12138.todo.R -import cn.super12138.todo.ui.TodoDefaults -import cn.super12138.todo.ui.components.LargeTopAppBarScaffold -import cn.super12138.todo.ui.pages.settings.components.Settings +import cn.super12138.todo.ui.components.TopAppBarScaffold +import cn.super12138.todo.ui.components.RoundedScreenContainer +import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @OptIn(ExperimentalMaterial3Api::class) @@ -24,26 +19,22 @@ fun SettingsDeveloperOptions( onNavigateUp: () -> Unit, modifier: Modifier = Modifier ) { - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_developer_options), onBack = onNavigateUp, - scrollBehavior = scrollBehavior, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, ) { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .verticalScroll(rememberScrollState()) - ) { - Settings { - SettingsItem( - leadingIconRes = R.drawable.ic_padding, - title = stringResource(R.string.pref_padding), - onClick = toPaddingPage - ) + RoundedScreenContainer(Modifier.padding(innerPadding)) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsItem( + leadingIconRes = R.drawable.ic_padding, + title = stringResource(R.string.pref_padding), + onClick = toPaddingPage, + topRounded = true, + bottomRounded = true + ) + } } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt index 96251d1..80677ea 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsInterfaceInteraction.kt @@ -1,13 +1,9 @@ package cn.super12138.todo.ui.pages.settings -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize 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.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -17,17 +13,16 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import cn.super12138.todo.R import cn.super12138.todo.constants.Constants import cn.super12138.todo.logic.datastore.DataStoreManager import cn.super12138.todo.logic.model.SortingMethod -import cn.super12138.todo.ui.TodoDefaults -import cn.super12138.todo.ui.components.LargeTopAppBarScaffold -import cn.super12138.todo.ui.pages.settings.components.Settings +import cn.super12138.todo.ui.components.RoundedScreenContainer +import cn.super12138.todo.ui.components.TopAppBarScaffold 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.SettingsPlainBox import cn.super12138.todo.ui.pages.settings.components.SettingsRadioDialog @@ -48,76 +43,79 @@ fun SettingsInterface( val context = LocalContext.current val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() var showSortingMethodDialog by rememberSaveable { mutableStateOf(false) } - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.pref_interface_interaction), onBack = onNavigateUp, - scrollBehavior = scrollBehavior, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = modifier, ) { innerPadding -> - Column( - Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .verticalScroll(rememberScrollState()) - ) { - SettingsCategory(stringResource(R.string.pref_category_todo_list)) - Settings { - SwitchSettingsItem( - leadingIconRes = R.drawable.ic_checklist, - title = stringResource(R.string.pref_show_completed), - description = stringResource(R.string.pref_show_completed_desc), - checked = showCompleted, - onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } }, - ) - SettingsItem( - leadingIconRes = R.drawable.ic_sort, - title = stringResource(R.string.pref_sorting_method), - description = SortingMethod.fromId(sortingMethod).getDisplayName(context), - onClick = { showSortingMethodDialog = true } - ) + RoundedScreenContainer(Modifier.padding(innerPadding)) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsCategory(title = stringResource(R.string.pref_category_todo_list), first = true) + SwitchSettingsItem( + leadingIconRes = R.drawable.ic_checklist, + title = stringResource(R.string.pref_show_completed), + description = stringResource(R.string.pref_show_completed_desc), + checked = showCompleted, + onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } }, + topRounded = true + ) + } + item { + SettingsItem( + leadingIconRes = R.drawable.ic_sort, + title = stringResource(R.string.pref_sorting_method), + description = SortingMethod.fromId(sortingMethod).getDisplayName(context), + onClick = { showSortingMethodDialog = true }, + bottomRounded = 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 { - SettingsRadioOptions( - id = it.id, - text = it.getDisplayName(context) - ) + val sortingList = remember { + SortingMethod.entries.map { + SettingsRadioOptions( + 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 } - ) } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt index 6299153..45f90aa 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsMain.kt @@ -1,21 +1,16 @@ package cn.super12138.todo.ui.pages.settings -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize 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.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import cn.super12138.todo.R -import cn.super12138.todo.ui.TodoDefaults -import cn.super12138.todo.ui.components.LargeTopAppBarScaffold -import cn.super12138.todo.ui.pages.settings.components.Settings +import cn.super12138.todo.ui.components.TopAppBarScaffold +import cn.super12138.todo.ui.components.RoundedScreenContainer +import cn.super12138.todo.ui.pages.settings.components.SettingsContainer import cn.super12138.todo.ui.pages.settings.components.SettingsItem @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @@ -28,46 +23,50 @@ fun SettingsMain( onNavigateUp: () -> Unit, modifier: Modifier = Modifier ) { - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() - LargeTopAppBarScaffold( + TopAppBarScaffold( title = stringResource(R.string.page_settings), - scrollBehavior = scrollBehavior, onBack = onNavigateUp, - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection) + modifier = modifier ) { innerPadding -> - Column( - Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = TodoDefaults.screenHorizontalPadding) - .verticalScroll(rememberScrollState()) - ) { - Settings { - SettingsItem( - 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 - ) + RoundedScreenContainer(Modifier.padding(innerPadding)) { + SettingsContainer(Modifier.fillMaxSize()) { + item { + SettingsItem( + leadingIconRes = R.drawable.ic_palette, + title = stringResource(R.string.pref_appearance), + description = stringResource(R.string.pref_appearance_desc), + onClick = toAppearancePage, + topRounded = true + ) + } + 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 + ) + } } } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt index f82c2fe..ff1c36e 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -20,19 +21,15 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip 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.CompositingStrategy -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.sp import cn.super12138.todo.ui.TodoDefaults +import cn.super12138.todo.utils.drawFadedEdge +import cn.super12138.todo.utils.getPartialRoundedShape @Composable fun RowSettingsItem( @@ -41,13 +38,14 @@ fun RowSettingsItem( title: String, description: String? = null, trailingContent: (@Composable () -> Unit)? = null, - shape: Shape = MaterialTheme.shapes.small, - background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + background: Color = TodoDefaults.ContainerColor, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, scrollState: ScrollState = rememberScrollState(), fadedEdgeWidth: Dp, - maskColor: Color = MaterialTheme.colorScheme.background, + maskColor: Color = TodoDefaults.ContainerColor, + topRounded: Boolean = false, + bottomRounded: Boolean = false, content: @Composable RowScope.() -> Unit ) { MoreContentSettingsItem( @@ -55,8 +53,9 @@ fun RowSettingsItem( title = title, description = description, trailingContent = trailingContent, - shape = shape, background = background, + topRounded = topRounded, + bottomRounded = bottomRounded, modifier = modifier ) { Row( @@ -94,12 +93,13 @@ fun LazyRowSettingsItem( title: String, description: String? = null, trailingContent: (@Composable () -> Unit)? = null, - shape: Shape = MaterialTheme.shapes.small, - background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + background: Color = TodoDefaults.ContainerColor, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, fadedEdgeWidth: Dp, - maskColor: Color = MaterialTheme.colorScheme.background, + maskColor: Color = TodoDefaults.ContainerColor, + topRounded: Boolean = false, + bottomRounded: Boolean = false, content: LazyListScope.() -> Unit ) { MoreContentSettingsItem( @@ -107,7 +107,8 @@ fun LazyRowSettingsItem( title = title, description = description, trailingContent = trailingContent, - shape = shape, + topRounded = topRounded, + bottomRounded = bottomRounded, background = background, modifier = modifier ) { @@ -145,15 +146,18 @@ fun MoreContentSettingsItem( title: String, description: String? = null, trailingContent: (@Composable () -> Unit)? = null, - shape: Shape = MaterialTheme.shapes.small, - background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + background: Color = TodoDefaults.ContainerColor, + shape: CornerBasedShape = TodoDefaults.SettingsItemDefaultShape, + roundedShape: CornerBasedShape = TodoDefaults.SettingsItemRoundedShape, + topRounded: Boolean = false, + bottomRounded: Boolean = false, content: @Composable () -> Unit ) { Column( modifier = modifier .fillMaxWidth() .wrapContentHeight() - .clip(shape) + .clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape)) .background(background) .padding( horizontal = TodoDefaults.settingsItemHorizontalPadding, @@ -195,22 +199,3 @@ fun MoreContentSettingsItem( 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 - ) -} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsCategory.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsCategory.kt index 9d58ae4..099ec10 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsCategory.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsCategory.kt @@ -8,20 +8,23 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import cn.super12138.todo.ui.TodoDefaults @Composable fun SettingsCategory( title: String, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + first: Boolean = false, ) { Box( modifier = modifier .fillMaxWidth() .padding( - top = TodoDefaults.settingsItemVerticalPadding / 2, - start = TodoDefaults.settingsItemHorizontalPadding, - end = TodoDefaults.settingsItemHorizontalPadding, + top = if (first) 0.dp else TodoDefaults.settingsItemVerticalPadding, + start = TodoDefaults.settingsItemHorizontalPadding / 2, + end = TodoDefaults.settingsItemHorizontalPadding / 2, + bottom = TodoDefaults.settingsItemVerticalPadding / 2 ) ) { Text( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsContainer.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsContainer.kt index 36b9062..5f4cce1 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsContainer.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsContainer.kt @@ -1,28 +1,35 @@ package cn.super12138.todo.ui.pages.settings.components import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.MaterialTheme +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp import cn.super12138.todo.ui.TodoDefaults -@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun Settings( +fun SettingsContainer( modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit, + state: LazyListState = rememberLazyListState(), + content: LazyListScope.() -> Unit, ) { - Column( - verticalArrangement = Arrangement.spacedBy(2.dp), + LazyColumn( + verticalArrangement = Arrangement.spacedBy(TodoDefaults.settingsItemPadding), + state = state, modifier = modifier - .padding(vertical = TodoDefaults.screenVerticalPadding) - .clip(MaterialTheme.shapes.extraLarge), - content = content - ) + ) { + item { + Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding)) + } + + content() + + item { + Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding)) + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt index d146ccb..5d0d8bb 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsDialogs.kt @@ -76,7 +76,7 @@ fun RadioItem( modifier .fillMaxWidth() .height(56.dp) - .clip(MaterialTheme.shapes.small) + .clip(TodoDefaults.SettingsItemDefaultShape) .background(MaterialTheme.colorScheme.surfaceContainerHighest) .selectable( selected = selected, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt index 2a9381e..e9f2093 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -17,7 +18,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalView @@ -26,6 +26,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.sp import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.utils.VibrationUtils +import cn.super12138.todo.utils.getPartialRoundedShape @Composable fun SettingsItem( @@ -33,6 +34,8 @@ fun SettingsItem( @DrawableRes leadingIconRes: Int, title: String, description: String? = null, + topRounded: Boolean = false, + bottomRounded: Boolean = false, enableClick: Boolean = true, onClick: () -> Unit = {} ) { @@ -41,6 +44,8 @@ fun SettingsItem( title = title, description = description, trailingContent = null, + topRounded = topRounded, + bottomRounded = bottomRounded, enableClick = enableClick, onClick = onClick, modifier = modifier @@ -53,6 +58,8 @@ fun SettingsItem( leadingIcon: ImageVector? = null, title: String, description: String? = null, + topRounded: Boolean = false, + bottomRounded: Boolean = false, enableClick: Boolean = true, onClick: () -> Unit = {} ) { @@ -61,6 +68,8 @@ fun SettingsItem( title = title, description = description, trailingContent = null, + topRounded = topRounded, + bottomRounded = bottomRounded, enableClick = enableClick, onClick = onClick, modifier = modifier @@ -74,7 +83,9 @@ fun SettingsItem( title: String, description: String? = null, trailingContent: (@Composable () -> Unit)? = null, - background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + background: Color = TodoDefaults.ContainerColor, + topRounded: Boolean = false, + bottomRounded: Boolean = false, enableClick: Boolean = true, onClick: () -> Unit = {} ) { @@ -93,6 +104,8 @@ fun SettingsItem( description = description, trailingContent = trailingContent, background = background, + topRounded = topRounded, + bottomRounded = bottomRounded, enableClick = enableClick, onClick = onClick, modifier = modifier @@ -106,7 +119,9 @@ fun SettingsItem( title: String, description: String? = null, trailingContent: (@Composable () -> Unit)? = null, - background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + background: Color = TodoDefaults.ContainerColor, + topRounded: Boolean = false, + bottomRounded: Boolean = false, enableClick: Boolean = true, onClick: () -> Unit = {} ) { @@ -125,6 +140,8 @@ fun SettingsItem( description = description, trailingContent = trailingContent, background = background, + topRounded = topRounded, + bottomRounded = bottomRounded, enableClick = enableClick, onClick = onClick, modifier = modifier @@ -139,8 +156,11 @@ fun SettingsItem( title: String, description: String? = null, trailingContent: (@Composable () -> Unit)? = null, - shape: Shape = MaterialTheme.shapes.small, - background: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + background: Color = TodoDefaults.ContainerColor, + shape: CornerBasedShape = TodoDefaults.SettingsItemDefaultShape, + roundedShape: CornerBasedShape = TodoDefaults.SettingsItemRoundedShape, + topRounded: Boolean = false, + bottomRounded: Boolean = false, enableClick: Boolean = true, onClick: () -> Unit = {}, ) { @@ -149,7 +169,7 @@ fun SettingsItem( modifier = modifier .fillMaxWidth() .wrapContentHeight() - .clip(shape) + .clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape)) .clickable( enabled = enableClick, onClick = { diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsPlainBox.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsPlainBox.kt index 630a754..af68ab6 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsPlainBox.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsPlainBox.kt @@ -30,9 +30,8 @@ fun SettingsPlainBox( .fillMaxWidth() .wrapContentHeight() .padding( - start = TodoDefaults.settingsItemHorizontalPadding, - end = TodoDefaults.settingsItemHorizontalPadding, - bottom = TodoDefaults.settingsItemVerticalPadding + vertical = TodoDefaults.settingsItemVerticalPadding, + horizontal = TodoDefaults.settingsItemHorizontalPadding / 2 ), ) { Icon( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt index dba0e6f..3ab8e77 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SwitchSettingsItem.kt @@ -17,10 +17,11 @@ fun SwitchSettingsItem( @DrawableRes leadingIconRes: Int, title: String, description: String? = null, + topRounded: Boolean = false, + bottomRounded: Boolean = false, checked: Boolean, onCheckedChange: (Boolean) -> Unit ) { - val view = LocalView.current SettingsItem( leadingIcon = painterResource(leadingIconRes), title = title, @@ -28,14 +29,13 @@ fun SwitchSettingsItem( trailingContent = { Switch( checked = checked, - onCheckedChange = { - VibrationUtils.performHapticFeedback(view) - onCheckedChange(it) - }, + onCheckedChange = null, modifier = Modifier.padding(start = TodoDefaults.settingsItemHorizontalPadding / 2) ) }, onClick = { onCheckedChange(!checked) }, + topRounded = topRounded, + bottomRounded = bottomRounded, modifier = modifier, ) } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt index ef9a0e9..3ba95bc 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/contrast/ContrastPicker.kt @@ -35,15 +35,19 @@ import cn.super12138.todo.utils.VibrationUtils @Composable fun ContrastPicker( + modifier: Modifier = Modifier, currentContrast: ContrastLevel, onContrastChange: (ContrastLevel) -> Unit, - modifier: Modifier = Modifier + topRounded: Boolean = false, + bottomRounded: Boolean = false ) { val view = LocalView.current val context = LocalContext.current MoreContentSettingsItem( title = stringResource(R.string.pref_contrast_level), description = stringResource(R.string.pref_contrast_level_desc), + topRounded = topRounded, + bottomRounded = bottomRounded, modifier = modifier ) { val contrastLevelName = diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt index 8cdec0c..b777ade 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/darkmode/DarkModePicker.kt @@ -13,14 +13,17 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp 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.theme.DarkMode @Composable fun DarkModePicker( + modifier: Modifier = Modifier, currentDarkMode: () -> DarkMode, onDarkModeChange: (darkMode: DarkMode) -> Unit, - modifier: Modifier = Modifier + topRounded: Boolean = false, + bottomRounded: Boolean = false ) { val context = LocalContext.current val isInDarkTheme = isSystemInDarkTheme() @@ -31,7 +34,9 @@ fun DarkModePicker( title = stringResource(R.string.pref_dark_mode), description = stringResource(R.string.pref_dark_mode_desc), horizontalArrangement = Arrangement.spacedBy(5.dp), - fadedEdgeWidth = 8.dp, + fadedEdgeWidth = TodoDefaults.fadedEdgeWidth, + topRounded = topRounded, + bottomRounded = bottomRounded, modifier = modifier ) { items(items = darkModeList, key = { it.id }) { diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt index 9a0d5b8..c0b266d 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/appearance/palette/PalettePicker.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp 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.theme.ContrastLevel import cn.super12138.todo.ui.theme.DarkMode @@ -18,12 +19,14 @@ import cn.super12138.todo.ui.theme.PaletteStyle @Composable fun PalettePicker( + modifier: Modifier = Modifier, currentPalette: () -> PaletteStyle, onPaletteChange: (paletteStyle: PaletteStyle) -> Unit, isDynamicColor: Boolean, isDarkMode: DarkMode, contrastLevel: ContrastLevel, - modifier: Modifier = Modifier + topRounded: Boolean = false, + bottomRounded: Boolean = false ) { val paletteOptions = remember { PaletteStyle.entries.toList() } @@ -31,7 +34,9 @@ fun PalettePicker( title = stringResource(R.string.pref_palette_style), description = stringResource(R.string.pref_palette_style_desc), horizontalArrangement = Arrangement.spacedBy(5.dp), - fadedEdgeWidth = 8.dp, + fadedEdgeWidth = TodoDefaults.fadedEdgeWidth, + topRounded = topRounded, + bottomRounded = bottomRounded, modifier = modifier ) { items(items = paletteOptions, key = { it.id }) { diff --git a/app/src/main/kotlin/cn/super12138/todo/utils/TodoExt.kt b/app/src/main/kotlin/cn/super12138/todo/utils/TodoExt.kt index 65d5c37..d04dcda 100644 --- a/app/src/main/kotlin/cn/super12138/todo/utils/TodoExt.kt +++ b/app/src/main/kotlin/cn/super12138/todo/utils/TodoExt.kt @@ -1,9 +1,16 @@ package cn.super12138.todo.utils +import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable 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.drawscope.ContentDrawScope +import androidx.compose.ui.unit.Dp import cn.super12138.todo.logic.model.Priority @Composable @@ -15,4 +22,37 @@ fun Priority.containerColor(): Color = Priority.Default -> MaterialTheme.colorScheme.secondary Priority.Important -> MaterialTheme.colorScheme.tertiary Priority.Urgent -> MaterialTheme.colorScheme.error - } \ No newline at end of file + } + +@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 + ) +} \ No newline at end of file