diff --git a/app/src/main/java/com/sadellie/unitto/screens/common/UnittoListItem.kt b/app/src/main/java/com/sadellie/unitto/screens/common/UnittoListItem.kt index 17966702..5e905942 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/common/UnittoListItem.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/common/UnittoListItem.kt @@ -23,13 +23,6 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowDropDown @@ -37,6 +30,7 @@ import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch @@ -48,92 +42,12 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.sadellie.unitto.R -/** - * Basic list item for settings screen. By default only has label and support text, clickable. - * This component can be easily modified if you provide additional component to it, - * for example a switch or a checkbox. - * - * @param modifier Modifier that will be applied to a Row. - * @param label Main text. - * @param supportText Text that is located below label. - * @param trailingItem Additional composable, icons for example. - * @param leadingItem Additional composable: buttons, switches, checkboxes or something else. - */ -@Composable -private fun BasicUnittoListItem( - modifier: Modifier = Modifier, - label: String, - supportText: String? = null, - leadingItem: @Composable() (RowScope.() -> Unit) = {}, - trailingItem: @Composable() (RowScope.() -> Unit) = {} -) { - Row( - modifier = modifier - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - leadingItem() - Column( - Modifier.weight(1f), // This makes additional composable to be seen - ) { - Text( - modifier = Modifier - .fillMaxWidth(), - text = label, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - supportText?.let { - Text( - modifier = Modifier - .fillMaxWidth(), - text = it, - style = MaterialTheme.typography.bodySmall, - maxLines = 2, - overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - trailingItem() - } -} - -/** - * Represents one item in list on Settings screen. - * - * @param modifier Modifier that will be applied to a Row. - * @param label Main text. - * @param supportText Text that is located below label. - * @param onClick Action to perform when user clicks on this component (whole component is clickable). - */ -@Composable -fun UnittoListItem( - modifier: Modifier = Modifier, - label: String, - supportText: String? = null, - onClick: () -> Unit, -) = BasicUnittoListItem( - modifier = modifier - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(), - onClick = onClick - ) - .padding(horizontal = 16.dp, vertical = 12.dp), - label = label, - supportText = supportText -) - /** * Represents one item in list on Settings screen. * @@ -149,18 +63,22 @@ fun UnittoListItem( supportText: String? = null, switchState: Boolean, onSwitchChange: (Boolean) -> Unit -) = BasicUnittoListItem( - modifier = Modifier - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(), - onClick = { onSwitchChange(!switchState) } - ) - .padding(horizontal = 16.dp, vertical = 12.dp), - label = label, - supportText = supportText ) { - Switch(checked = switchState, onCheckedChange = { onSwitchChange(it) }) + ListItem( + modifier = Modifier + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(), + onClick = { onSwitchChange(!switchState) } + ), + headlineText = { Text(label) }, + supportingText = { supportText?.let { Text(text = it) } }, + trailingContent = { + Switch( + checked = switchState, + onCheckedChange = { onSwitchChange(it) }) + } + ) } /** @@ -181,87 +99,60 @@ fun UnittoListItem( selected: T, onSelectedChange: (T) -> Unit ) { - BasicUnittoListItem( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - label = label, - supportText = supportText - ) { - var dropDownExpanded by rememberSaveable { mutableStateOf(false) } - var currentOption by rememberSaveable { mutableStateOf(selected) } - val dropDownRotation: Float by animateFloatAsState( - targetValue = if (dropDownExpanded) 180f else 0f, - animationSpec = tween(easing = FastOutSlowInEasing) - ) + var dropDownExpanded by rememberSaveable { mutableStateOf(false) } + var currentOption by rememberSaveable { mutableStateOf(selected) } + val dropDownRotation: Float by animateFloatAsState( + targetValue = if (dropDownExpanded) 180f else 0f, + animationSpec = tween(easing = FastOutSlowInEasing) + ) - ExposedDropdownMenuBox( - modifier = Modifier, - expanded = dropDownExpanded, - onExpandedChange = { dropDownExpanded = it } - ) { - OutlinedTextField( - modifier = Modifier.widthIn(1.dp), - value = allOptions[currentOption] ?: selected.toString(), - onValueChange = {}, - readOnly = true, - singleLine = true, - enabled = false, - colors = TextFieldDefaults.outlinedTextFieldColors( - disabledBorderColor = MaterialTheme.colorScheme.outline, - disabledTextColor = MaterialTheme.colorScheme.onSurface, - disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant - ), - trailingIcon = { - Icon( - imageVector = Icons.Outlined.ArrowDropDown, - modifier = Modifier.rotate(dropDownRotation), - contentDescription = stringResource(R.string.drop_down_description) - ) - } - ) - ExposedDropdownMenu( - modifier = Modifier.exposedDropdownSize(), + ListItem( + headlineText = { Text(label) }, + supportingText = { supportText?.let { Text(text = it) } }, + trailingContent = { + ExposedDropdownMenuBox( + modifier = Modifier, expanded = dropDownExpanded, - onDismissRequest = { dropDownExpanded = false } + onExpandedChange = { dropDownExpanded = it } ) { - allOptions.forEach { - DropdownMenuItem( - text = { Text(it.value) }, - onClick = { - currentOption = it.key - onSelectedChange(it.key) - dropDownExpanded = false - } - ) + OutlinedTextField( + modifier = Modifier.widthIn(1.dp), + value = allOptions[currentOption] ?: selected.toString(), + onValueChange = {}, + readOnly = true, + singleLine = true, + enabled = false, + textStyle = MaterialTheme.typography.bodyLarge, + colors = TextFieldDefaults.outlinedTextFieldColors( + disabledBorderColor = MaterialTheme.colorScheme.outline, + disabledTextColor = MaterialTheme.colorScheme.onSurface, + disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant + ), + trailingIcon = { + Icon( + imageVector = Icons.Outlined.ArrowDropDown, + modifier = Modifier.rotate(dropDownRotation), + contentDescription = stringResource(R.string.drop_down_description) + ) + } + ) + ExposedDropdownMenu( + modifier = Modifier.exposedDropdownSize(), + expanded = dropDownExpanded, + onDismissRequest = { dropDownExpanded = false } + ) { + allOptions.forEach { + DropdownMenuItem( + text = { Text(it.value) }, + onClick = { + currentOption = it.key + onSelectedChange(it.key) + dropDownExpanded = false + } + ) + } } } } - } -} - -/** - * Composable with leading and trailing items. - * - * @param label Main text. - * @param modifier Modifier that will be applied to a Row. - * @param trailingItem Additional composable, icons for example. - * @param leadingItem Additional composable: buttons, switches, checkboxes or something else. - */ -@Composable -fun UnittoListItem( - label: String, - supportText: String? = null, - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 12.dp), - leadingItem: @Composable RowScope.() -> Unit = {}, - trailingItem: @Composable RowScope.() -> Unit = {} -) { - BasicUnittoListItem( - modifier = modifier.padding(paddingValues), - label = label, - supportText = supportText, - trailingItem = trailingItem, - leadingItem = leadingItem ) } - - diff --git a/app/src/main/java/com/sadellie/unitto/screens/second/components/UnitListItem.kt b/app/src/main/java/com/sadellie/unitto/screens/second/components/UnitListItem.kt index d0ad08f1..019dacd6 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/second/components/UnitListItem.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/second/components/UnitListItem.kt @@ -26,8 +26,11 @@ import androidx.compose.animation.with import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -36,19 +39,21 @@ import androidx.compose.material.icons.filled.FavoriteBorder import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable 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.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.sadellie.unitto.R import com.sadellie.unitto.data.units.AbstractUnit -import com.sadellie.unitto.screens.common.UnittoListItem /** * Represents one list item. Once clicked will navigate up. @@ -68,7 +73,7 @@ private fun BasicUnitListItem( shortNameLabel: String ) { var isFavorite: Boolean by rememberSaveable { mutableStateOf(unit.isFavorite) } - Box( + Column( modifier = Modifier .clickable( interactionSource = remember { MutableInteractionSource() }, @@ -77,35 +82,56 @@ private fun BasicUnitListItem( ) .padding(horizontal = 12.dp) ) { - UnittoListItem( + Row( modifier = Modifier - .background( - if (isSelected) MaterialTheme.colorScheme.secondaryContainer else Color.Transparent, - RoundedCornerShape(24.dp) - ), - paddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp), - label = stringResource(unit.displayName), - supportText = shortNameLabel, - trailingItem = { - AnimatedContent( + .background( + if (isSelected) MaterialTheme.colorScheme.secondaryContainer else Color.Transparent, + RoundedCornerShape(24.dp) + ).padding(paddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp)) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Column( + Modifier.weight(1f), // This makes additional composable to be seen + ) { + Text( modifier = Modifier - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(false), - onClick = { favoriteAction(unit); isFavorite = !isFavorite } - ), - targetState = isFavorite, - transitionSpec = { - (scaleIn() with scaleOut()).using(SizeTransform(clip = false)) - } - ) { - Icon( - if (unit.isFavorite) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder, - contentDescription = stringResource(R.string.favorite_button_description) + .fillMaxWidth(), + text = stringResource(unit.displayName), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + shortNameLabel.let { + Text( + modifier = Modifier + .fillMaxWidth(), + text = it, + style = MaterialTheme.typography.bodySmall, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurfaceVariant ) } } - ) + AnimatedContent( + modifier = Modifier + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(false), + onClick = { favoriteAction(unit); isFavorite = !isFavorite } + ), + targetState = isFavorite, + transitionSpec = { + (scaleIn() with scaleOut()).using(SizeTransform(clip = false)) + } + ) { + Icon( + if (unit.isFavorite) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder, + contentDescription = stringResource(R.string.favorite_button_description) + ) + } + } } } diff --git a/app/src/main/java/com/sadellie/unitto/screens/setttings/SettingsScreen.kt b/app/src/main/java/com/sadellie/unitto/screens/setttings/SettingsScreen.kt index 359bb0e9..aae0813e 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/setttings/SettingsScreen.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/setttings/SettingsScreen.kt @@ -18,12 +18,16 @@ package com.sadellie.unitto.screens.setttings +import androidx.compose.foundation.clickable import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text 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.platform.LocalContext import androidx.compose.ui.res.stringResource import com.sadellie.unitto.BuildConfig @@ -62,53 +66,53 @@ fun SettingsScreen( // THEME item { - UnittoListItem( - label = stringResource(R.string.unit_groups_setting), - onClick = { navControllerAction(UNIT_GROUPS_SCREEN) } + ListItem( + headlineText = { Text(stringResource(R.string.unit_groups_setting)) }, + modifier = Modifier.clickable { navControllerAction(UNIT_GROUPS_SCREEN) } ) } // PRECISION item { - UnittoListItem( - label = stringResource(R.string.precision_setting), - supportText = stringResource(R.string.precision_setting_support), - onClick = { dialogState = DialogState.PRECISION } + ListItem( + headlineText = { Text(stringResource(R.string.precision_setting)) }, + supportingText = { Text(stringResource(R.string.precision_setting_support)) }, + modifier = Modifier.clickable { dialogState = DialogState.PRECISION } ) } // SEPARATOR item { - UnittoListItem( - label = stringResource(R.string.separator_setting), - supportText = stringResource(R.string.separator_setting_support), - onClick = { dialogState = DialogState.SEPARATOR } + ListItem( + headlineText = { Text(stringResource(R.string.separator_setting)) }, + supportingText = { Text(stringResource(R.string.separator_setting_support)) }, + modifier = Modifier.clickable { dialogState = DialogState.SEPARATOR } ) } // OUTPUT FORMAT item { - UnittoListItem( - label = stringResource(R.string.output_format_setting), - supportText = stringResource(R.string.output_format_setting_support), - onClick = { dialogState = DialogState.OUTPUT_FORMAT } + ListItem( + headlineText = { Text(stringResource(R.string.output_format_setting)) }, + supportingText = { Text(stringResource(R.string.output_format_setting_support)) }, + modifier = Modifier.clickable { dialogState = DialogState.OUTPUT_FORMAT } ) } // THEME item { - UnittoListItem( - label = stringResource(R.string.theme_setting), - supportText = stringResource(R.string.theme_setting_support), - onClick = { navControllerAction(THEMES_SCREEN) } + ListItem( + headlineText = { Text(stringResource(R.string.theme_setting)) }, + supportingText = { Text(stringResource(R.string.theme_setting_support)) }, + modifier = Modifier.clickable { navControllerAction(THEMES_SCREEN) } ) } // CURRENCY RATE NOTE item { - UnittoListItem( - label = stringResource(R.string.currency_rates_note_setting), - onClick = { dialogState = DialogState.CURRENCY_RATE } + ListItem( + headlineText = { Text(stringResource(R.string.currency_rates_note_setting)) }, + modifier = Modifier.clickable { dialogState = DialogState.CURRENCY_RATE } ) } @@ -117,9 +121,9 @@ fun SettingsScreen( // TERMS AND CONDITIONS item { - UnittoListItem( - label = stringResource(R.string.terms_and_conditions), - onClick = { + ListItem( + headlineText = { Text(stringResource(R.string.terms_and_conditions)) }, + modifier = Modifier.clickable { openLink( mContext, "http://sadellie.github.io/unitto/terms-app.html" @@ -130,9 +134,9 @@ fun SettingsScreen( // PRIVACY POLICY item { - UnittoListItem( - label = stringResource(R.string.privacy_policy), - onClick = { + ListItem( + headlineText = { Text(stringResource(R.string.privacy_policy)) }, + modifier = Modifier.clickable { openLink( mContext, "http://sadellie.github.io/unitto/privacy-app.html" @@ -154,28 +158,28 @@ fun SettingsScreen( // THIRD PARTY item { - UnittoListItem( - label = stringResource(R.string.third_party_licenses), - onClick = { navControllerAction(ABOUT_SCREEN) } + ListItem( + headlineText = { Text(stringResource(R.string.third_party_licenses)) }, + modifier = Modifier.clickable { navControllerAction(ABOUT_SCREEN) } ) } // RATE THIS APP if (BuildConfig.STORE_LINK.isNotEmpty()) { item { - UnittoListItem( - label = stringResource(R.string.rate_this_app), - onClick = { openLink(mContext, BuildConfig.STORE_LINK) } + ListItem( + headlineText = { Text(stringResource(R.string.rate_this_app)) }, + modifier = Modifier.clickable { openLink(mContext, BuildConfig.STORE_LINK) } ) } } // APP VERSION item { - UnittoListItem( - label = stringResource(R.string.app_version_name_setting), - supportText = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})", - onClick = {} + ListItem( + headlineText = { Text(stringResource(R.string.app_version_name_setting)) }, + supportingText = { Text("${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") }, + modifier = Modifier.clickable {} ) } } diff --git a/app/src/main/java/com/sadellie/unitto/screens/setttings/UnitGroupsScreen.kt b/app/src/main/java/com/sadellie/unitto/screens/setttings/UnitGroupsScreen.kt index 0f5af04b..b812fe36 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/setttings/UnitGroupsScreen.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/setttings/UnitGroupsScreen.kt @@ -33,7 +33,10 @@ import androidx.compose.material.icons.filled.DragIndicator import androidx.compose.material.icons.filled.RemoveCircle import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember @@ -42,9 +45,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sadellie.unitto.R -import com.sadellie.unitto.screens.common.UnittoLargeTopAppBar import com.sadellie.unitto.screens.common.Header -import com.sadellie.unitto.screens.common.UnittoListItem +import com.sadellie.unitto.screens.common.UnittoLargeTopAppBar import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.detectReorder import org.burnoutcrew.reorderable.detectReorderAfterLongPress @@ -87,19 +89,21 @@ fun UnitGroupsScreen( ) val cornerRadius = animateDpAsState(if (isDragging) 16.dp else 0.dp) - UnittoListItem( + ListItem( + headlineText = { Text(stringResource(item.res)) }, modifier = Modifier .padding(horizontal = cornerRadius.value) .clip(RoundedCornerShape(cornerRadius.value)) - .background(background.value) .clickable { viewModel.hideUnitGroup(item) } .detectReorderAfterLongPress(state), - label = stringResource(item.res), - leadingItem = { + colors = ListItemDefaults.colors( + containerColor = background.value + ), + leadingContent = { Icon( Icons.Default.RemoveCircle, stringResource(R.string.disable_unit_group_description), - tint= MaterialTheme.colorScheme.outline, + tint = MaterialTheme.colorScheme.outline, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(false), @@ -107,11 +111,11 @@ fun UnitGroupsScreen( ) ) }, - trailingItem = { + trailingContent = { Icon( Icons.Default.DragIndicator, stringResource(R.string.reorder_unit_group_description), - tint= MaterialTheme.colorScheme.outline, + tint = MaterialTheme.colorScheme.outline, modifier = Modifier .clickable( interactionSource = remember { MutableInteractionSource() }, @@ -120,7 +124,7 @@ fun UnitGroupsScreen( ) .detectReorder(state) ) - } + }, ) } } @@ -133,17 +137,17 @@ fun UnitGroupsScreen( } items(hiddenUnits.value, { it }) { - UnittoListItem( + ListItem( modifier = Modifier .background(MaterialTheme.colorScheme.surface) .clickable { viewModel.returnUnitGroup(it) } .animateItemPlacement(), - label = stringResource(it.res), - trailingItem = { + headlineText = { Text(stringResource(it.res)) }, + trailingContent = { Icon( Icons.Default.AddCircleOutline, stringResource(R.string.enable_unit_group_description), - tint= MaterialTheme.colorScheme.outline, + tint = MaterialTheme.colorScheme.outline, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(false),