feat: add visible edit affordances to clickable cards (Closes #143) (#145)

This commit was merged in pull request #145.
This commit is contained in:
2026-04-06 17:43:32 +02:00
parent 8a0625dc3e
commit c17adb4936
5 changed files with 79 additions and 14 deletions
@@ -169,6 +169,10 @@
<string name="language_expert">Expert</string> <string name="language_expert">Expert</string>
<string name="language_native">Native</string> <string name="language_native">Native</string>
<!-- Edit affordances -->
<string name="edit_content_desc">Tap to edit</string>
<string name="resource_edit_hint">Tap Character Header above to edit</string>
<!-- Dice roll content descriptions --> <!-- Dice roll content descriptions -->
<string name="roll_dice_content_desc">Roll dice</string> <string name="roll_dice_content_desc">Roll dice</string>
@@ -2,6 +2,8 @@ package org.shahondin1624.lib.components.charactermodel
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -39,9 +41,22 @@ fun CharacterHeader(
else Modifier else Modifier
) )
) { ) {
when (windowSizeClass) { Box(modifier = Modifier.fillMaxWidth()) {
WindowSizeClass.Compact -> CompactHeader(characterData, spacing) when (windowSizeClass) {
WindowSizeClass.Medium, WindowSizeClass.Expanded -> ExpandedHeader(characterData, spacing) WindowSizeClass.Compact -> CompactHeader(characterData, spacing)
WindowSizeClass.Medium, WindowSizeClass.Expanded -> ExpandedHeader(characterData, spacing)
}
if (onEdit != null) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(Res.string.edit_content_desc),
modifier = Modifier
.align(Alignment.TopEnd)
.padding(8.dp)
.size(20.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
} }
} }
} }
@@ -110,9 +125,10 @@ private fun ExpandedHeader(characterData: CharacterData, spacing: androidx.compo
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
// Right: Age and gender // Right: Age and gender (add right padding to avoid overlapping edit icon)
Row( Row(
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(end = 28.dp)
) { ) {
DetailChip(label = stringResource(Res.string.label_age), value = characterData.age.toString()) DetailChip(label = stringResource(Res.string.label_age), value = characterData.age.toString())
DetailChip(label = stringResource(Res.string.label_gender), value = characterData.gender) DetailChip(label = stringResource(Res.string.label_gender), value = characterData.gender)
@@ -9,6 +9,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.shahondin1624.lib.components.TestTags import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants import org.shahondin1624.lib.components.UiConstants
@@ -34,9 +35,25 @@ fun ResourcePanel(characterData: CharacterData, edge: Int) {
.fillMaxWidth() .fillMaxWidth()
.testTag(TestTags.PANEL_RESOURCES) .testTag(TestTags.PANEL_RESOURCES)
) { ) {
when (windowSizeClass) { Column {
WindowSizeClass.Compact -> CompactResources(characterData, edge, spacing) when (windowSizeClass) {
WindowSizeClass.Medium, WindowSizeClass.Expanded -> ExpandedResources(characterData, edge, spacing) WindowSizeClass.Compact -> CompactResources(characterData, edge, spacing)
WindowSizeClass.Medium, WindowSizeClass.Expanded -> ExpandedResources(
characterData,
edge,
spacing
)
}
// Hint text pointing users to the Character Header for editing
Text(
text = stringResource(Res.string.resource_edit_hint),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 6.dp)
)
} }
} }
} }
@@ -4,8 +4,11 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -49,14 +52,14 @@ fun Attribute(
modifier = cardModifier, modifier = cardModifier,
elevation = elevation elevation = elevation
) { ) {
AttributeCardContent(attribute, textColor, onRoll) AttributeCardContent(attribute, textColor, onRoll, showEditIcon = true)
} }
} else { } else {
Card( Card(
modifier = cardModifier, modifier = cardModifier,
elevation = elevation elevation = elevation
) { ) {
AttributeCardContent(attribute, textColor, onRoll) AttributeCardContent(attribute, textColor, onRoll, showEditIcon = false)
} }
} }
} }
@@ -65,13 +68,24 @@ fun Attribute(
private fun AttributeCardContent( private fun AttributeCardContent(
attribute: Attribute, attribute: Attribute,
textColor: Color, textColor: Color,
onRoll: (DiceRoll) -> Unit onRoll: (DiceRoll) -> Unit,
showEditIcon: Boolean
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
if (showEditIcon) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(Res.string.edit_content_desc),
modifier = Modifier
.padding(start = SMALL_PADDING)
.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Row( Row(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
@@ -9,10 +9,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -62,14 +65,14 @@ fun Talent(
modifier = cardModifier, modifier = cardModifier,
elevation = elevation elevation = elevation
) { ) {
TalentCardContent(talent, attributes, textColor, onRoll) TalentCardContent(talent, attributes, textColor, onRoll, showEditIcon = true)
} }
} else { } else {
Card( Card(
modifier = cardModifier, modifier = cardModifier,
elevation = elevation elevation = elevation
) { ) {
TalentCardContent(talent, attributes, textColor, onRoll) TalentCardContent(talent, attributes, textColor, onRoll, showEditIcon = false)
} }
} }
} }
@@ -79,7 +82,8 @@ private fun TalentCardContent(
talent: TalentDefinition, talent: TalentDefinition,
attributes: Attributes, attributes: Attributes,
textColor: Color, textColor: Color,
onRoll: (DiceRoll) -> Unit onRoll: (DiceRoll) -> Unit,
showEditIcon: Boolean
) { ) {
var showSpecMenu by remember { mutableStateOf(false) } var showSpecMenu by remember { mutableStateOf(false) }
val hasSpec = talent.specialization != null val hasSpec = talent.specialization != null
@@ -89,6 +93,16 @@ private fun TalentCardContent(
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
if (showEditIcon) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(Res.string.edit_content_desc),
modifier = Modifier
.padding(start = SMALL_PADDING)
.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Row( Row(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)