perf: 优化设置页面的性能

This commit is contained in:
Super12138 2025-06-30 13:10:48 +08:00
parent 711d8a19c5
commit 5c25a4657d
10 changed files with 259 additions and 181 deletions

View file

@ -34,7 +34,7 @@ android {
applicationId = "cn.super12138.todo" applicationId = "cn.super12138.todo"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 730 versionCode = 733
versionName = "2.1.2" versionName = "2.1.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

View file

@ -224,8 +224,6 @@ fun TodoEditorPage(
} }
} }
} }
//Spacer(Modifier.size(20.dp))
} }
} }

View file

@ -1,11 +1,9 @@
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.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.lazy.LazyColumn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Balance import androidx.compose.material.icons.outlined.Balance
import androidx.compose.material.icons.outlined.Numbers import androidx.compose.material.icons.outlined.Numbers
@ -50,61 +48,68 @@ fun SettingsAbout(
val context = LocalContext.current val context = LocalContext.current
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
Column( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .padding(innerPadding)
.verticalScroll(rememberScrollState())
) { ) {
var clickCount by remember { mutableIntStateOf(0) } item {
var lastClickTime by remember { mutableLongStateOf(0L) } var clickCount by remember { mutableIntStateOf(0) }
var lastClickTime by remember { mutableLongStateOf(0L) }
LaunchedEffect(clickCount) { LaunchedEffect(clickCount) {
if (clickCount > 0) { if (clickCount > 0) {
lastClickTime = System.currentTimeMillis() lastClickTime = System.currentTimeMillis()
val currentClickTime = lastClickTime val currentClickTime = lastClickTime
delay(300L) delay(300L)
if (currentClickTime == lastClickTime) { if (currentClickTime == lastClickTime) {
clickCount = 0 clickCount = 0
}
}
}
SettingsItem(
leadingIcon = Icons.Outlined.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
} }
} }
)
SettingsItem( SettingsItem(
leadingIcon = Icons.Outlined.Person4, leadingIcon = Icons.Outlined.Numbers,
title = stringResource(R.string.pref_developer), title = stringResource(R.string.pref_app_version),
description = stringResource(R.string.developer_name), description = SystemUtils.getAppVersion(context),
onClick = { uriHandler.openUri(Constants.DEVELOPER_GITHUB) } onClick = {
) clickCount++
SettingsItem( if (clickCount == 5) {
leadingIcon = GitHubIcon, if ((System.currentTimeMillis() % 2) == 0.toLong()) {
title = stringResource(R.string.pref_view_on_github), Toast.makeText(context, "🍨", Toast.LENGTH_SHORT).show()
description = stringResource(R.string.pref_view_on_github_desc), } else {
onClick = { uriHandler.openUri(Constants.GITHUB_REPO) } Toast.makeText(context, "", Toast.LENGTH_SHORT).show()
) }
SettingsItem( clickCount = 0
leadingIcon = Icons.Outlined.Balance, }
title = stringResource(R.string.pref_licence), }
description = stringResource(R.string.pref_licence_desc), )
onClick = toLicencePage }
) item {
SettingsItem(
leadingIcon = Icons.Outlined.Person4,
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(
leadingIcon = Icons.Outlined.Balance,
title = stringResource(R.string.pref_licence),
description = stringResource(R.string.pref_licence_desc),
onClick = toLicencePage
)
}
} }
} }
} }

View file

@ -4,11 +4,9 @@ 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.lazy.LazyColumn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FileDownload import androidx.compose.material.icons.outlined.FileDownload
import androidx.compose.material.icons.outlined.FileUpload import androidx.compose.material.icons.outlined.FileUpload
@ -110,30 +108,34 @@ fun SettingsData(
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState) },
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { innerPadding -> ) { innerPadding ->
Column( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .padding(innerPadding)
.verticalScroll(rememberScrollState())
) { ) {
SettingsItem( item {
leadingIcon = Icons.Outlined.FileDownload, SettingsItem(
title = stringResource(R.string.pref_backup), leadingIcon = Icons.Outlined.FileDownload,
description = stringResource(R.string.pref_backup_desc), title = stringResource(R.string.pref_backup),
onClick = { description = stringResource(R.string.pref_backup_desc),
backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip") onClick = {
} backupLauncher.launch("Todo-backup-${SystemUtils.getTime()}.zip")
) }
SettingsItem( )
leadingIcon = Icons.Outlined.FileUpload, }
title = stringResource(R.string.pref_restore), item {
description = stringResource(R.string.pref_restore_desc), SettingsItem(
onClick = { leadingIcon = Icons.Outlined.FileUpload,
restoreLauncher.launch(arrayOf("application/zip")) title = stringResource(R.string.pref_restore),
} description = stringResource(R.string.pref_restore_desc),
) onClick = {
restoreLauncher.launch(arrayOf("application/zip"))
}
)
}
} }
} }
ConfirmDialog( ConfirmDialog(
visible = showRestoreDialog, visible = showRestoreDialog,
icon = Icons.Outlined.RestartAlt, icon = Icons.Outlined.RestartAlt,

View file

@ -1,10 +1,8 @@
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.lazy.LazyColumn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Sort import androidx.compose.material.icons.automirrored.outlined.Sort
import androidx.compose.material.icons.outlined.Checklist import androidx.compose.material.icons.outlined.Checklist
@ -59,43 +57,54 @@ fun SettingsInterface(
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { innerPadding -> ) { innerPadding ->
Column( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .padding(innerPadding)
.verticalScroll(rememberScrollState())
) { ) {
SettingsCategory(stringResource(R.string.pref_category_todo_list)) item {
SwitchSettingsItem( SettingsCategory(stringResource(R.string.pref_category_todo_list))
checked = showCompleted, }
leadingIcon = Icons.Outlined.Checklist, item {
title = stringResource(R.string.pref_show_completed), SwitchSettingsItem(
description = stringResource(R.string.pref_show_completed_desc), checked = showCompleted,
onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } }, leadingIcon = Icons.Outlined.Checklist,
) title = stringResource(R.string.pref_show_completed),
SettingsItem( description = stringResource(R.string.pref_show_completed_desc),
leadingIcon = Icons.AutoMirrored.Outlined.Sort, onCheckedChange = { scope.launch { DataStoreManager.setShowCompleted(it) } },
title = stringResource(R.string.pref_sorting_method), )
description = SortingMethod.fromId(sortingMethod).getDisplayName(context), }
onClick = { showSortingMethodDialog = true } item {
) SettingsItem(
leadingIcon = Icons.AutoMirrored.Outlined.Sort,
title = stringResource(R.string.pref_sorting_method),
description = SortingMethod.fromId(sortingMethod).getDisplayName(context),
onClick = { showSortingMethodDialog = true }
)
}
SettingsCategory(stringResource(R.string.pref_category_global)) item {
SwitchSettingsItem( SettingsCategory(stringResource(R.string.pref_category_global))
checked = secureMode, }
leadingIcon = Icons.Outlined.Shield, item {
title = stringResource(R.string.pref_secure_mode), SwitchSettingsItem(
description = stringResource(R.string.pref_secure_mode_desc), checked = secureMode,
onCheckedChange = { scope.launch { DataStoreManager.setSecureMode(it) } } leadingIcon = Icons.Outlined.Shield,
) title = stringResource(R.string.pref_secure_mode),
SwitchSettingsItem( description = stringResource(R.string.pref_secure_mode_desc),
checked = hapticFeedback, onCheckedChange = { scope.launch { DataStoreManager.setSecureMode(it) } }
leadingIcon = Icons.Outlined.Vibration, )
title = stringResource(R.string.pref_haptic_feedback), }
description = stringResource(R.string.pref_haptic_feedback_desc), item {
onCheckedChange = { scope.launch { DataStoreManager.setHapticFeedback(it) } } SwitchSettingsItem(
) checked = hapticFeedback,
SettingsPlainBox(stringResource(R.string.pref_haptic_feedback_more_info)) leadingIcon = Icons.Outlined.Vibration,
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))
}
} }
} }

View file

@ -1,10 +1,8 @@
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.lazy.LazyColumn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ColorLens import androidx.compose.material.icons.outlined.ColorLens
import androidx.compose.material.icons.outlined.Dns import androidx.compose.material.icons.outlined.Dns
@ -14,6 +12,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults 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.components.LargeTopAppBarScaffold import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
@ -33,38 +32,46 @@ fun SettingsMain(
LargeTopAppBarScaffold( LargeTopAppBarScaffold(
title = stringResource(R.string.page_settings), title = stringResource(R.string.page_settings),
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
onBack = onNavigateUp onBack = onNavigateUp,
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding -> ) { innerPadding ->
Column( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .padding(innerPadding)
.verticalScroll(rememberScrollState())
) { ) {
SettingsItem( item {
leadingIcon = Icons.Outlined.ColorLens, SettingsItem(
title = stringResource(R.string.pref_appearance), leadingIcon = Icons.Outlined.ColorLens,
description = stringResource(R.string.pref_appearance_desc), title = stringResource(R.string.pref_appearance),
onClick = toAppearancePage description = stringResource(R.string.pref_appearance_desc),
) onClick = toAppearancePage
SettingsItem( )
leadingIcon = Icons.Outlined.ViewComfy, }
title = stringResource(R.string.pref_interface_interaction), item {
description = stringResource(R.string.pref_interface_interaction_desc), SettingsItem(
onClick = toInterfacePage leadingIcon = Icons.Outlined.ViewComfy,
) title = stringResource(R.string.pref_interface_interaction),
SettingsItem( description = stringResource(R.string.pref_interface_interaction_desc),
leadingIcon = Icons.Outlined.Dns, onClick = toInterfacePage
title = stringResource(R.string.pref_data), )
description = stringResource(R.string.pref_data_desc), }
onClick = toDataPage item {
) SettingsItem(
SettingsItem( leadingIcon = Icons.Outlined.Dns,
leadingIcon = Icons.Outlined.Info, title = stringResource(R.string.pref_data),
title = stringResource(R.string.pref_about), description = stringResource(R.string.pref_data_desc),
description = stringResource(R.string.pref_about_desc), onClick = toDataPage
onClick = toAboutPage )
) }
item {
SettingsItem(
leadingIcon = Icons.Outlined.Info,
title = stringResource(R.string.pref_about),
description = stringResource(R.string.pref_about_desc),
onClick = toAboutPage
)
}
} }
} }
} }

View file

@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.RowScope
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.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -82,6 +84,55 @@ fun RowSettingsItem(
} }
} }
@Composable
fun LazyRowSettingsItem(
modifier: Modifier = Modifier,
leadingIcon: (@Composable () -> Unit)? = null,
title: String,
description: String? = null,
trailingContent: (@Composable () -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.large,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top,
fadedEdgeWidth: Dp,
maskColor: Color = MaterialTheme.colorScheme.background,
content: LazyListScope.() -> Unit
) {
MoreContentSettingsItem(
leadingIcon = leadingIcon,
title = title,
description = description,
trailingContent = trailingContent,
shape = shape,
modifier = modifier
) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
// 渲染到离屏缓冲区是为了确保边缘淡出的 alpha 效果仅应用于文本本身,而不影响该可组合项下方绘制的内容(例如窗口背景)。
.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
.drawWithContent {
// 需要调用 drawContent因为它用于将内容绘制到布局中的接收器作用域允许内容穿插在其他画布操作之间绘制。
// 如果未调用 drawContent则不会绘制该布局的内容。
drawContent()
drawFadedEdge(
edgeWidth = fadedEdgeWidth,
maskColor = maskColor,
leftEdge = true
)
drawFadedEdge(
edgeWidth = fadedEdgeWidth,
maskColor = maskColor,
leftEdge = false
)
},
horizontalArrangement = horizontalArrangement,
verticalAlignment = verticalAlignment,
content = content
)
}
}
@Composable @Composable
fun MoreContentSettingsItem( fun MoreContentSettingsItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View file

@ -33,9 +33,7 @@ fun SwitchSettingsItem(
modifier = Modifier.padding(start = TodoDefaults.settingsItemHorizontalPadding / 2) modifier = Modifier.padding(start = TodoDefaults.settingsItemHorizontalPadding / 2)
) )
}, },
onClick = { onClick = { onCheckedChange(!checked) },
onCheckedChange(!checked)
},
modifier = modifier modifier = modifier
) )
} }

View file

@ -9,7 +9,7 @@ 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.pages.settings.components.RowSettingsItem 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
@ -20,38 +20,44 @@ fun DarkModePicker(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val isInDarkTheme = isSystemInDarkTheme() val isInDarkTheme = isSystemInDarkTheme()
RowSettingsItem( LazyRowSettingsItem(
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 = 8.dp,
modifier = modifier modifier = modifier
) { ) {
DarkModeItem( item {
icon = DarkMode.FollowSystem.icon, DarkModeItem(
name = DarkMode.FollowSystem.getDisplayName(context), icon = DarkMode.FollowSystem.icon,
contentColor = if (isInDarkTheme) Color.White else Color.Black, name = DarkMode.FollowSystem.getDisplayName(context),
containerColor = if (isInDarkTheme) Color.Black else Color.White, contentColor = if (isInDarkTheme) Color.White else Color.Black,
selected = currentDarkMode == DarkMode.FollowSystem, containerColor = if (isInDarkTheme) Color.Black else Color.White,
onSelect = { onDarkModeChange(DarkMode.FollowSystem) } selected = currentDarkMode == DarkMode.FollowSystem,
) onSelect = { onDarkModeChange(DarkMode.FollowSystem) }
)
}
DarkModeItem( item {
icon = DarkMode.Light.icon, DarkModeItem(
name = DarkMode.Light.getDisplayName(context), icon = DarkMode.Light.icon,
contentColor = Color.Black, name = DarkMode.Light.getDisplayName(context),
containerColor = Color.White, contentColor = Color.Black,
selected = currentDarkMode == DarkMode.Light, containerColor = Color.White,
onSelect = { onDarkModeChange(DarkMode.Light) } selected = currentDarkMode == DarkMode.Light,
) onSelect = { onDarkModeChange(DarkMode.Light) }
)
}
DarkModeItem( item {
icon = DarkMode.Dark.icon, DarkModeItem(
name = DarkMode.Dark.getDisplayName(context), icon = DarkMode.Dark.icon,
contentColor = Color.White, name = DarkMode.Dark.getDisplayName(context),
containerColor = Color.Black, contentColor = Color.White,
selected = currentDarkMode == DarkMode.Dark, containerColor = Color.Black,
onSelect = { onDarkModeChange(DarkMode.Dark) } selected = currentDarkMode == DarkMode.Dark,
) onSelect = { onDarkModeChange(DarkMode.Dark) }
)
}
} }
} }

View file

@ -8,7 +8,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.pages.settings.components.RowSettingsItem 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
import cn.super12138.todo.ui.theme.PaletteStyle import cn.super12138.todo.ui.theme.PaletteStyle
@ -27,7 +27,7 @@ fun PalettePicker(
PaletteStyle.entries.toList() PaletteStyle.entries.toList()
} }
RowSettingsItem( LazyRowSettingsItem(
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),
@ -35,18 +35,20 @@ fun PalettePicker(
modifier = modifier modifier = modifier
) { ) {
paletteOptions.forEach { paletteStyle -> paletteOptions.forEach { paletteStyle ->
PaletteItem( item {
isDynamicColor = isDynamicColor, PaletteItem(
isDark = when (isDarkMode) { isDynamicColor = isDynamicColor,
DarkMode.FollowSystem -> isSystemInDarkTheme() isDark = when (isDarkMode) {
DarkMode.Light -> false DarkMode.FollowSystem -> isSystemInDarkTheme()
DarkMode.Dark -> true DarkMode.Light -> false
}, DarkMode.Dark -> true
paletteStyle = paletteStyle, },
selected = currentPalette == paletteStyle, paletteStyle = paletteStyle,
contrastLevel = contrastLevel, selected = currentPalette == paletteStyle,
onSelect = { onPaletteChange(paletteStyle) } contrastLevel = contrastLevel,
) onSelect = { onPaletteChange(paletteStyle) }
)
}
} }
} }
} }