From 6e6f5477e1693653998d743b1427cfcdb102d925 Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Thu, 23 Oct 2025 20:54:08 +0530 Subject: [PATCH] feat(ui): redesign theme selection UI --- .../settingsScreen/components/ThemeDialog.kt | 151 ------------------ .../components/ThemePickerListItem.kt | 121 ++++++++++---- .../screens/AppearanceSettings.kt | 19 --- app/src/main/res/values/strings.xml | 2 +- 4 files changed, 89 insertions(+), 204 deletions(-) delete mode 100644 app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemeDialog.kt diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemeDialog.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemeDialog.kt deleted file mode 100644 index 244612b..0000000 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemeDialog.kt +++ /dev/null @@ -1,151 +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 . - */ - -package org.nsh07.pomodoro.ui.settingsScreen.components - -import androidx.compose.animation.AnimatedContent -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.selection.selectable -import androidx.compose.foundation.selection.selectableGroup -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.ListItem -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.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.dp -import org.nsh07.pomodoro.R -import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors -import org.nsh07.pomodoro.ui.theme.CustomColors.selectedListItemColors -import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape -import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape -import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape - -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) -@Composable -fun ThemeDialog( - themeMap: Map>, - reverseThemeMap: Map, - theme: String, - setShowThemeDialog: (Boolean) -> Unit, - onThemeChange: (String) -> Unit -) { - val selectedOption = - remember { mutableIntStateOf(themeMap[theme]!!.second) } - - val context = LocalContext.current - - BasicAlertDialog( - onDismissRequest = { setShowThemeDialog(false) } - ) { - Surface( - modifier = Modifier - .wrapContentWidth() - .wrapContentHeight(), - shape = shapes.extraLarge, - color = colorScheme.surfaceContainer, - tonalElevation = AlertDialogDefaults.TonalElevation - ) { - Column(modifier = Modifier.padding(24.dp)) { - Text( - text = stringResource(R.string.choose_theme), - style = MaterialTheme.typography.headlineSmall - ) - Spacer(modifier = Modifier.height(16.dp)) - Column( - verticalArrangement = Arrangement.spacedBy(2.dp), - modifier = Modifier.selectableGroup() - ) { - themeMap.entries.forEachIndexed { index: Int, pair: Map.Entry> -> - val text = pair.value.second - val selected = text == selectedOption.intValue - - ListItem( - leadingContent = { - AnimatedContent(selected) { - if (it) - Icon(painterResource(R.drawable.check), null) - else - Icon(painterResource(pair.value.first), null) - } - }, - headlineContent = { - Text( - text = stringResource(text), - style = MaterialTheme.typography.bodyLarge - ) - }, - colors = if (!selected) listItemColors else selectedListItemColors, - modifier = Modifier - .height(64.dp) - .clip( - when (index) { - 0 -> topListItemShape - themeMap.size - 1 -> bottomListItemShape - else -> middleListItemShape - } - ) - .selectable( - selected = (text == selectedOption.intValue), - onClick = { - selectedOption.intValue = text - onThemeChange( - reverseThemeMap[context.getString( - selectedOption.intValue - )]!! - ) - }, - role = Role.RadioButton - ) - ) - } - } - Spacer(modifier = Modifier.height(16.dp)) - TextButton( - shapes = ButtonDefaults.shapes(), - onClick = { setShowThemeDialog(false) }, - modifier = Modifier.align(Alignment.End) - ) { - Text(stringResource(R.string.ok)) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemePickerListItem.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemePickerListItem.kt index 7cf5594..0392765 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemePickerListItem.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/components/ThemePickerListItem.kt @@ -17,54 +17,109 @@ 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.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ButtonGroupDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.Text +import androidx.compose.material3.ToggleButton 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.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp import org.nsh07.pomodoro.R +import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors +import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape +import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape +import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun ThemePickerListItem( theme: String, - themeMap: Map>, - reverseThemeMap: Map, items: Int, index: Int, onThemeChange: (String) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { - var showDialog by rememberSaveable { mutableStateOf(false) } - - if (showDialog) { - ThemeDialog( - themeMap = themeMap, - reverseThemeMap = reverseThemeMap, - theme = theme, - setShowThemeDialog = { showDialog = it }, - onThemeChange = onThemeChange + val themeMap: Map> = remember { + mapOf( + "auto" to Pair( + R.drawable.brightness_auto, + R.string.system_default + ), + "light" to Pair(R.drawable.light_mode, R.string.light), + "dark" to Pair(R.drawable.dark_mode, R.string.dark) ) } - ClickableListItem( - leadingContent = { - Icon( - painter = painterResource(themeMap[theme]!!.first), - contentDescription = null - ) - }, - headlineContent = { Text(stringResource(R.string.theme)) }, - supportingContent = { - Text(stringResource(themeMap[theme]!!.second)) - }, - items = items, - index = index, - modifier = modifier.fillMaxWidth() - ) { showDialog = true } -} \ No newline at end of file + Column( + modifier + .clip( + when (index) { + 0 -> topListItemShape + items - 1 -> bottomListItemShape + else -> middleListItemShape + }, + ), + ) { + ListItem( + leadingContent = { + AnimatedContent(themeMap[theme]!!.first) { + Icon( + painter = painterResource(it), + contentDescription = null, + ) + } + }, + headlineContent = { Text(stringResource(R.string.theme)) }, + colors = listItemColors, + ) + + val options = themeMap.toList() + val selectedIndex = options.indexOf(Pair(theme, themeMap[theme])) + + Row( + horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween), + modifier = Modifier + .background(listItemColors.containerColor) + .padding(start = 52.dp, end = 16.dp, bottom = 8.dp) + ) { + options.forEachIndexed { index, theme -> + val isSelected = selectedIndex == index + ToggleButton( + checked = isSelected, + onCheckedChange = { onThemeChange(theme.first) }, + modifier = Modifier + .weight(1f) + .semantics { role = Role.RadioButton }, + shapes = + when (index) { + 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() + options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() + else -> ButtonGroupDefaults.connectedMiddleButtonShapes() + }, + ) { + Text( + stringResource(theme.second.second), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + } + } +} diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt index 81175af..eb55fb8 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/screens/AppearanceSettings.kt @@ -37,7 +37,6 @@ import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -69,22 +68,6 @@ fun AppearanceSettings( onBack: () -> Unit, modifier: Modifier = Modifier ) { - val themeMap: Map> = remember { - mapOf( - "auto" to Pair( - R.drawable.brightness_auto, - R.string.system_default - ), - "light" to Pair(R.drawable.light_mode, R.string.light), - "dark" to Pair(R.drawable.dark_mode, R.string.dark) - ) - } - val reverseThemeMap: Map = mapOf( - stringResource(R.string.system_default) to "auto", - stringResource(R.string.light) to "light", - stringResource(R.string.dark) to "dark" - ) - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { @@ -128,8 +111,6 @@ fun AppearanceSettings( item { ThemePickerListItem( theme = preferencesState.theme, - themeMap = themeMap, - reverseThemeMap = reverseThemeMap, onThemeChange = onThemeChange, items = 3, index = 1, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 975c571..cf51e15 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -65,7 +65,7 @@ Stop alarm Current timer session is complete. Tap anywhere to stop the alarm. Stop Alarm? - System default + System Theme Timer Timer progress