This commit is contained in:
Sad Ellie 2022-08-14 20:38:49 +03:00
parent c965a68198
commit a3a5d9ec27
4 changed files with 178 additions and 253 deletions

View File

@ -23,13 +23,6 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource 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.foundation.layout.widthIn
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowDropDown 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.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
@ -48,92 +42,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.R 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. * Represents one item in list on Settings screen.
* *
@ -149,18 +63,22 @@ fun UnittoListItem(
supportText: String? = null, supportText: String? = null,
switchState: Boolean, switchState: Boolean,
onSwitchChange: (Boolean) -> Unit onSwitchChange: (Boolean) -> Unit
) = BasicUnittoListItem( ) {
ListItem(
modifier = Modifier modifier = Modifier
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(), indication = rememberRipple(),
onClick = { onSwitchChange(!switchState) } onClick = { onSwitchChange(!switchState) }
),
headlineText = { Text(label) },
supportingText = { supportText?.let { Text(text = it) } },
trailingContent = {
Switch(
checked = switchState,
onCheckedChange = { onSwitchChange(it) })
}
) )
.padding(horizontal = 16.dp, vertical = 12.dp),
label = label,
supportText = supportText
) {
Switch(checked = switchState, onCheckedChange = { onSwitchChange(it) })
} }
/** /**
@ -181,11 +99,6 @@ fun <T> UnittoListItem(
selected: T, selected: T,
onSelectedChange: (T) -> Unit onSelectedChange: (T) -> Unit
) { ) {
BasicUnittoListItem(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
label = label,
supportText = supportText
) {
var dropDownExpanded by rememberSaveable { mutableStateOf(false) } var dropDownExpanded by rememberSaveable { mutableStateOf(false) }
var currentOption by rememberSaveable { mutableStateOf(selected) } var currentOption by rememberSaveable { mutableStateOf(selected) }
val dropDownRotation: Float by animateFloatAsState( val dropDownRotation: Float by animateFloatAsState(
@ -193,6 +106,10 @@ fun <T> UnittoListItem(
animationSpec = tween(easing = FastOutSlowInEasing) animationSpec = tween(easing = FastOutSlowInEasing)
) )
ListItem(
headlineText = { Text(label) },
supportingText = { supportText?.let { Text(text = it) } },
trailingContent = {
ExposedDropdownMenuBox( ExposedDropdownMenuBox(
modifier = Modifier, modifier = Modifier,
expanded = dropDownExpanded, expanded = dropDownExpanded,
@ -205,6 +122,7 @@ fun <T> UnittoListItem(
readOnly = true, readOnly = true,
singleLine = true, singleLine = true,
enabled = false, enabled = false,
textStyle = MaterialTheme.typography.bodyLarge,
colors = TextFieldDefaults.outlinedTextFieldColors( colors = TextFieldDefaults.outlinedTextFieldColors(
disabledBorderColor = MaterialTheme.colorScheme.outline, disabledBorderColor = MaterialTheme.colorScheme.outline,
disabledTextColor = MaterialTheme.colorScheme.onSurface, disabledTextColor = MaterialTheme.colorScheme.onSurface,
@ -236,32 +154,5 @@ fun <T> UnittoListItem(
} }
} }
} }
}
/**
* 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
) )
} }

View File

@ -26,8 +26,11 @@ import androidx.compose.animation.with
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource 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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons 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.material.ripple.rememberRipple
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.R import com.sadellie.unitto.R
import com.sadellie.unitto.data.units.AbstractUnit import com.sadellie.unitto.data.units.AbstractUnit
import com.sadellie.unitto.screens.common.UnittoListItem
/** /**
* Represents one list item. Once clicked will navigate up. * Represents one list item. Once clicked will navigate up.
@ -68,7 +73,7 @@ private fun BasicUnitListItem(
shortNameLabel: String shortNameLabel: String
) { ) {
var isFavorite: Boolean by rememberSaveable { mutableStateOf(unit.isFavorite) } var isFavorite: Boolean by rememberSaveable { mutableStateOf(unit.isFavorite) }
Box( Column(
modifier = Modifier modifier = Modifier
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
@ -77,16 +82,38 @@ private fun BasicUnitListItem(
) )
.padding(horizontal = 12.dp) .padding(horizontal = 12.dp)
) { ) {
UnittoListItem( Row(
modifier = Modifier modifier = Modifier
.background( .background(
if (isSelected) MaterialTheme.colorScheme.secondaryContainer else Color.Transparent, if (isSelected) MaterialTheme.colorScheme.secondaryContainer else Color.Transparent,
RoundedCornerShape(24.dp) RoundedCornerShape(24.dp)
), ).padding(paddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp))
paddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp), .fillMaxWidth(),
label = stringResource(unit.displayName), verticalAlignment = Alignment.CenterVertically,
supportText = shortNameLabel, horizontalArrangement = Arrangement.spacedBy(16.dp)
trailingItem = { ) {
Column(
Modifier.weight(1f), // This makes additional composable to be seen
) {
Text(
modifier = Modifier
.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( AnimatedContent(
modifier = Modifier modifier = Modifier
.clickable( .clickable(
@ -105,7 +132,6 @@ private fun BasicUnitListItem(
) )
} }
} }
)
} }
} }

View File

@ -18,12 +18,16 @@
package com.sadellie.unitto.screens.setttings package com.sadellie.unitto.screens.setttings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.lazy.LazyColumn 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.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.sadellie.unitto.BuildConfig import com.sadellie.unitto.BuildConfig
@ -62,53 +66,53 @@ fun SettingsScreen(
// THEME // THEME
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.unit_groups_setting), headlineText = { Text(stringResource(R.string.unit_groups_setting)) },
onClick = { navControllerAction(UNIT_GROUPS_SCREEN) } modifier = Modifier.clickable { navControllerAction(UNIT_GROUPS_SCREEN) }
) )
} }
// PRECISION // PRECISION
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.precision_setting), headlineText = { Text(stringResource(R.string.precision_setting)) },
supportText = stringResource(R.string.precision_setting_support), supportingText = { Text(stringResource(R.string.precision_setting_support)) },
onClick = { dialogState = DialogState.PRECISION } modifier = Modifier.clickable { dialogState = DialogState.PRECISION }
) )
} }
// SEPARATOR // SEPARATOR
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.separator_setting), headlineText = { Text(stringResource(R.string.separator_setting)) },
supportText = stringResource(R.string.separator_setting_support), supportingText = { Text(stringResource(R.string.separator_setting_support)) },
onClick = { dialogState = DialogState.SEPARATOR } modifier = Modifier.clickable { dialogState = DialogState.SEPARATOR }
) )
} }
// OUTPUT FORMAT // OUTPUT FORMAT
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.output_format_setting), headlineText = { Text(stringResource(R.string.output_format_setting)) },
supportText = stringResource(R.string.output_format_setting_support), supportingText = { Text(stringResource(R.string.output_format_setting_support)) },
onClick = { dialogState = DialogState.OUTPUT_FORMAT } modifier = Modifier.clickable { dialogState = DialogState.OUTPUT_FORMAT }
) )
} }
// THEME // THEME
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.theme_setting), headlineText = { Text(stringResource(R.string.theme_setting)) },
supportText = stringResource(R.string.theme_setting_support), supportingText = { Text(stringResource(R.string.theme_setting_support)) },
onClick = { navControllerAction(THEMES_SCREEN) } modifier = Modifier.clickable { navControllerAction(THEMES_SCREEN) }
) )
} }
// CURRENCY RATE NOTE // CURRENCY RATE NOTE
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.currency_rates_note_setting), headlineText = { Text(stringResource(R.string.currency_rates_note_setting)) },
onClick = { dialogState = DialogState.CURRENCY_RATE } modifier = Modifier.clickable { dialogState = DialogState.CURRENCY_RATE }
) )
} }
@ -117,9 +121,9 @@ fun SettingsScreen(
// TERMS AND CONDITIONS // TERMS AND CONDITIONS
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.terms_and_conditions), headlineText = { Text(stringResource(R.string.terms_and_conditions)) },
onClick = { modifier = Modifier.clickable {
openLink( openLink(
mContext, mContext,
"http://sadellie.github.io/unitto/terms-app.html" "http://sadellie.github.io/unitto/terms-app.html"
@ -130,9 +134,9 @@ fun SettingsScreen(
// PRIVACY POLICY // PRIVACY POLICY
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.privacy_policy), headlineText = { Text(stringResource(R.string.privacy_policy)) },
onClick = { modifier = Modifier.clickable {
openLink( openLink(
mContext, mContext,
"http://sadellie.github.io/unitto/privacy-app.html" "http://sadellie.github.io/unitto/privacy-app.html"
@ -154,28 +158,28 @@ fun SettingsScreen(
// THIRD PARTY // THIRD PARTY
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.third_party_licenses), headlineText = { Text(stringResource(R.string.third_party_licenses)) },
onClick = { navControllerAction(ABOUT_SCREEN) } modifier = Modifier.clickable { navControllerAction(ABOUT_SCREEN) }
) )
} }
// RATE THIS APP // RATE THIS APP
if (BuildConfig.STORE_LINK.isNotEmpty()) { if (BuildConfig.STORE_LINK.isNotEmpty()) {
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.rate_this_app), headlineText = { Text(stringResource(R.string.rate_this_app)) },
onClick = { openLink(mContext, BuildConfig.STORE_LINK) } modifier = Modifier.clickable { openLink(mContext, BuildConfig.STORE_LINK) }
) )
} }
} }
// APP VERSION // APP VERSION
item { item {
UnittoListItem( ListItem(
label = stringResource(R.string.app_version_name_setting), headlineText = { Text(stringResource(R.string.app_version_name_setting)) },
supportText = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})", supportingText = { Text("${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") },
onClick = {} modifier = Modifier.clickable {}
) )
} }
} }

View File

@ -33,7 +33,10 @@ import androidx.compose.material.icons.filled.DragIndicator
import androidx.compose.material.icons.filled.RemoveCircle import androidx.compose.material.icons.filled.RemoveCircle
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember 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.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.R 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.Header
import com.sadellie.unitto.screens.common.UnittoListItem import com.sadellie.unitto.screens.common.UnittoLargeTopAppBar
import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.detectReorder import org.burnoutcrew.reorderable.detectReorder
import org.burnoutcrew.reorderable.detectReorderAfterLongPress import org.burnoutcrew.reorderable.detectReorderAfterLongPress
@ -87,19 +89,21 @@ fun UnitGroupsScreen(
) )
val cornerRadius = animateDpAsState(if (isDragging) 16.dp else 0.dp) val cornerRadius = animateDpAsState(if (isDragging) 16.dp else 0.dp)
UnittoListItem( ListItem(
headlineText = { Text(stringResource(item.res)) },
modifier = Modifier modifier = Modifier
.padding(horizontal = cornerRadius.value) .padding(horizontal = cornerRadius.value)
.clip(RoundedCornerShape(cornerRadius.value)) .clip(RoundedCornerShape(cornerRadius.value))
.background(background.value)
.clickable { viewModel.hideUnitGroup(item) } .clickable { viewModel.hideUnitGroup(item) }
.detectReorderAfterLongPress(state), .detectReorderAfterLongPress(state),
label = stringResource(item.res), colors = ListItemDefaults.colors(
leadingItem = { containerColor = background.value
),
leadingContent = {
Icon( Icon(
Icons.Default.RemoveCircle, Icons.Default.RemoveCircle,
stringResource(R.string.disable_unit_group_description), stringResource(R.string.disable_unit_group_description),
tint= MaterialTheme.colorScheme.outline, tint = MaterialTheme.colorScheme.outline,
modifier = Modifier.clickable( modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false), indication = rememberRipple(false),
@ -107,11 +111,11 @@ fun UnitGroupsScreen(
) )
) )
}, },
trailingItem = { trailingContent = {
Icon( Icon(
Icons.Default.DragIndicator, Icons.Default.DragIndicator,
stringResource(R.string.reorder_unit_group_description), stringResource(R.string.reorder_unit_group_description),
tint= MaterialTheme.colorScheme.outline, tint = MaterialTheme.colorScheme.outline,
modifier = Modifier modifier = Modifier
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
@ -120,7 +124,7 @@ fun UnitGroupsScreen(
) )
.detectReorder(state) .detectReorder(state)
) )
} },
) )
} }
} }
@ -133,17 +137,17 @@ fun UnitGroupsScreen(
} }
items(hiddenUnits.value, { it }) { items(hiddenUnits.value, { it }) {
UnittoListItem( ListItem(
modifier = Modifier modifier = Modifier
.background(MaterialTheme.colorScheme.surface) .background(MaterialTheme.colorScheme.surface)
.clickable { viewModel.returnUnitGroup(it) } .clickable { viewModel.returnUnitGroup(it) }
.animateItemPlacement(), .animateItemPlacement(),
label = stringResource(it.res), headlineText = { Text(stringResource(it.res)) },
trailingItem = { trailingContent = {
Icon( Icon(
Icons.Default.AddCircleOutline, Icons.Default.AddCircleOutline,
stringResource(R.string.enable_unit_group_description), stringResource(R.string.enable_unit_group_description),
tint= MaterialTheme.colorScheme.outline, tint = MaterialTheme.colorScheme.outline,
modifier = Modifier.clickable( modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false), indication = rememberRipple(false),