feat(ui): improve color scheme selection UI
This commit is contained in:
@@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Nishant Mishra
|
|
||||||
*
|
|
||||||
* This file is part of Tomato - a minimalist pomodoro timer for Android.
|
|
||||||
*
|
|
||||||
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
|
|
||||||
* General Public License as published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
||||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
||||||
* Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with Tomato.
|
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.nsh07.pomodoro.ui.settingsScreen.components
|
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
|
||||||
import androidx.compose.material3.BasicAlertDialog
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.IconButtonDefaults
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
|
||||||
import androidx.compose.material3.MaterialTheme.shapes
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.util.fastForEach
|
|
||||||
import org.nsh07.pomodoro.R
|
|
||||||
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
||||||
@Composable
|
|
||||||
fun ColorPickerButton(
|
|
||||||
color: Color,
|
|
||||||
isSelected: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
IconButton(
|
|
||||||
shapes = IconButtonDefaults.shapes(),
|
|
||||||
colors = IconButtonDefaults.iconButtonColors(containerColor = color),
|
|
||||||
modifier = modifier.size(48.dp),
|
|
||||||
onClick = onClick
|
|
||||||
) {
|
|
||||||
AnimatedContent(isSelected) { isSelected ->
|
|
||||||
when (isSelected) {
|
|
||||||
true -> Icon(
|
|
||||||
painterResource(R.drawable.check),
|
|
||||||
tint = Color.Black,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
|
|
||||||
else ->
|
|
||||||
if (color == Color.White) Icon(
|
|
||||||
painterResource(R.drawable.colors),
|
|
||||||
tint = Color.Black,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
|
||||||
@Composable
|
|
||||||
fun ColorSchemePickerDialog(
|
|
||||||
currentColor: Color,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
setShowDialog: (Boolean) -> Unit,
|
|
||||||
onColorChange: (Color) -> Unit,
|
|
||||||
) {
|
|
||||||
val colorSchemes = listOf(
|
|
||||||
Color(0xfffeb4a7), Color(0xffffb3c0), Color(0xfffcaaff), Color(0xffb9c3ff),
|
|
||||||
Color(0xff62d3ff), Color(0xff44d9f1), Color(0xff52dbc9), Color(0xff78dd77),
|
|
||||||
Color(0xff9fd75c), Color(0xffc1d02d), Color(0xfffabd00), Color(0xffffb86e),
|
|
||||||
Color.White
|
|
||||||
)
|
|
||||||
|
|
||||||
BasicAlertDialog(
|
|
||||||
onDismissRequest = { setShowDialog(false) },
|
|
||||||
modifier = modifier
|
|
||||||
) {
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
.wrapContentWidth()
|
|
||||||
.wrapContentHeight(),
|
|
||||||
color = colorScheme.surfaceContainer,
|
|
||||||
shape = shapes.extraLarge,
|
|
||||||
tonalElevation = AlertDialogDefaults.TonalElevation
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.padding(24.dp)) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.choose_color_scheme),
|
|
||||||
style = MaterialTheme.typography.headlineSmall
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Column(Modifier.align(Alignment.CenterHorizontally)) {
|
|
||||||
(0..11 step 4).forEach {
|
|
||||||
Row {
|
|
||||||
colorSchemes.slice(it..it + 3).fastForEach { color ->
|
|
||||||
ColorPickerButton(
|
|
||||||
color,
|
|
||||||
color == currentColor,
|
|
||||||
modifier = Modifier.padding(4.dp)
|
|
||||||
) {
|
|
||||||
onColorChange(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColorPickerButton(
|
|
||||||
colorSchemes.last(),
|
|
||||||
colorSchemes.last() == currentColor,
|
|
||||||
modifier = Modifier.padding(4.dp)
|
|
||||||
) {
|
|
||||||
onColorChange(colorSchemes.last())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
|
|
||||||
TextButton(
|
|
||||||
shapes = ButtonDefaults.shapes(),
|
|
||||||
onClick = { setShowDialog(false) },
|
|
||||||
modifier = Modifier.align(Alignment.End)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.ok))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun ColorPickerDialogPreview() {
|
|
||||||
var currentColor by remember { mutableStateOf(Color(0xfffeb4a7)) }
|
|
||||||
TomatoTheme(darkTheme = true) {
|
|
||||||
ColorSchemePickerDialog(
|
|
||||||
currentColor,
|
|
||||||
setShowDialog = {},
|
|
||||||
onColorChange = { currentColor = it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,20 +17,39 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.ui.settingsScreen.components
|
package org.nsh07.pomodoro.ui.settingsScreen.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.SwitchDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
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.unit.dp
|
||||||
import org.nsh07.pomodoro.R
|
import org.nsh07.pomodoro.R
|
||||||
|
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
||||||
|
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors
|
||||||
|
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
|
||||||
|
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
|
||||||
|
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ColorSchemePickerListItem(
|
fun ColorSchemePickerListItem(
|
||||||
@@ -40,33 +59,131 @@ fun ColorSchemePickerListItem(
|
|||||||
onColorChange: (Color) -> Unit,
|
onColorChange: (Color) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
var showDialog by rememberSaveable { mutableStateOf(false) }
|
val colorSchemes = listOf(
|
||||||
|
Color(0xfffeb4a7), Color(0xffffb3c0), Color(0xfffcaaff), Color(0xffb9c3ff),
|
||||||
|
Color(0xff62d3ff), Color(0xff44d9f1), Color(0xff52dbc9), Color(0xff78dd77),
|
||||||
|
Color(0xff9fd75c), Color(0xffc1d02d), Color(0xfffabd00), Color(0xffffb86e),
|
||||||
|
Color.White
|
||||||
|
)
|
||||||
|
|
||||||
if (showDialog) {
|
Column(
|
||||||
ColorSchemePickerDialog(
|
modifier
|
||||||
currentColor = color,
|
.clip(
|
||||||
setShowDialog = { showDialog = it },
|
when (index) {
|
||||||
onColorChange = onColorChange
|
0 -> topListItemShape
|
||||||
|
items - 1 -> bottomListItemShape
|
||||||
|
else -> middleListItemShape
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
painterResource(R.drawable.colors),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
},
|
||||||
|
headlineContent = { Text("Dynamic color") },
|
||||||
|
supportingContent = { Text("Adapt theme colors from your wallpaper") },
|
||||||
|
trailingContent = {
|
||||||
|
val checked = color == colorSchemes.last()
|
||||||
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = {
|
||||||
|
if (it) onColorChange(colorSchemes.last())
|
||||||
|
else onColorChange(colorSchemes.first())
|
||||||
|
},
|
||||||
|
thumbContent = {
|
||||||
|
if (checked) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.check),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SwitchDefaults.IconSize),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.clear),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SwitchDefaults.IconSize),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = switchColors
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = listItemColors,
|
||||||
|
modifier = Modifier.clip(middleListItemShape)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(2.dp))
|
||||||
|
ListItem(
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.palette),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = colorScheme.primary
|
||||||
|
)
|
||||||
|
},
|
||||||
|
headlineContent = { Text(stringResource(R.string.color_scheme)) },
|
||||||
|
supportingContent = {
|
||||||
|
Text(
|
||||||
|
if (color == Color.White) stringResource(R.string.dynamic)
|
||||||
|
else stringResource(R.string.color)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = listItemColors,
|
||||||
|
modifier = Modifier.clip(middleListItemShape)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
ClickableListItem(
|
Column(
|
||||||
leadingContent = {
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
Icon(
|
modifier = Modifier
|
||||||
painter = painterResource(R.drawable.palette),
|
.background(listItemColors.containerColor)
|
||||||
contentDescription = null,
|
.padding(bottom = 8.dp)
|
||||||
tint = colorScheme.primary
|
) {
|
||||||
)
|
LazyRow(contentPadding = PaddingValues(horizontal = 48.dp)) {
|
||||||
},
|
items(colorSchemes.dropLast(1)) {
|
||||||
headlineContent = { Text(stringResource(R.string.color_scheme)) },
|
ColorPickerButton(
|
||||||
supportingContent = {
|
it,
|
||||||
Text(
|
it == color,
|
||||||
if (color == Color.White) stringResource(R.string.dynamic)
|
modifier = Modifier.padding(4.dp)
|
||||||
else stringResource(R.string.color)
|
) {
|
||||||
)
|
onColorChange(it)
|
||||||
},
|
}
|
||||||
items = items,
|
}
|
||||||
index = index,
|
}
|
||||||
modifier = modifier.fillMaxWidth()
|
}
|
||||||
) { showDialog = true }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun ColorPickerButton(
|
||||||
|
color: Color,
|
||||||
|
isSelected: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
shapes = IconButtonDefaults.shapes(),
|
||||||
|
colors = IconButtonDefaults.iconButtonColors(containerColor = color),
|
||||||
|
modifier = modifier.size(48.dp),
|
||||||
|
onClick = onClick
|
||||||
|
) {
|
||||||
|
AnimatedContent(isSelected) { isSelected ->
|
||||||
|
when (isSelected) {
|
||||||
|
true -> Icon(
|
||||||
|
painterResource(R.drawable.check),
|
||||||
|
tint = Color.Black,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
|
||||||
|
else ->
|
||||||
|
if (color == Color.White) Icon(
|
||||||
|
painterResource(R.drawable.colors),
|
||||||
|
tint = Color.Black,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user