Small UI adjustment over some screens.

This commit is contained in:
Thomas Andres Gomez 2023-09-19 10:16:15 +02:00
parent c8d6bfe4ef
commit 2e4c87c052
4 changed files with 194 additions and 168 deletions

View file

@ -22,11 +22,13 @@ class CharacterSheetParser @Inject constructor() {
// prepare a hash on the structure & function to get index in it.
lateinit var structure: Map<String, Int>
fun List<*>.parseInt(key: String): Int =
(getOrNull(structure.getValue(key)) as? String)?.toIntOrNull() ?: 0
// declare helper method to parse String
fun List<*>.parseString(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
// declare helper method to parse Int
fun List<*>.parseInt(key: String): Int? = parseString(key = key)?.toIntOrNull()
// parse the sheet
return sheet?.mapIndexedNotNull { index, item ->
@ -43,39 +45,39 @@ class CharacterSheetParser @Inject constructor() {
name = name,
hitPoint = item.parseString(HIT_POINT) ?: "1",
maxHitPoint = item.parseString(MAX_HIT_POINT) ?: "1",
armorClass = item.parseString(ARMOR_CLASS) ?: "0",
speed = item.parseInt(SPEED),
proficiency = item.parseInt(MASTERY),
strength = item.parseInt(STRENGTH),
dexterity = item.parseInt(DEXTERITY),
constitution = item.parseInt(CONSTITUTION),
intelligence = item.parseInt(INTELLIGENCE),
wisdom = item.parseInt(WISDOM),
charisma = item.parseInt(CHARISMA),
strengthSavingThrows = item.parseInt(STRENGTH_SAVING_THROW),
dexteritySavingThrows = item.parseInt(DEXTERITY_SAVING_THROW),
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW),
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW),
wisdomSavingThrows = item.parseInt(WISDOM_SAVING_THROW),
charismaSavingThrows = item.parseInt(CHARISMA_SAVING_THROW),
acrobatics = item.parseInt(ACROBATICS),
animalHandling = item.parseInt(ANIMAL_HANDLING),
arcana = item.parseInt(ARCANA),
athletics = item.parseInt(ATHLETICS),
deception = item.parseInt(DECEPTION),
history = item.parseInt(HISTORY),
insight = item.parseInt(INSIGHT),
intimidation = item.parseInt(INTIMIDATION),
investigation = item.parseInt(INVESTIGATION),
medicine = item.parseInt(MEDICINE),
nature = item.parseInt(NATURE),
perception = item.parseInt(PERCEPTION),
performance = item.parseInt(PERFORMANCE),
persuasion = item.parseInt(PERSUASION),
religion = item.parseInt(RELIGION),
sleightOfHand = item.parseInt(SLEIGHT_OF_HAND),
stealth = item.parseInt(STEALTH),
survival = item.parseInt(SURVIVAL),
armorClass = item.parseString(ARMOR_CLASS) ?: "10",
speed = item.parseInt(SPEED) ?: 10,
proficiency = item.parseInt(MASTERY) ?: 2,
strength = item.parseInt(STRENGTH) ?: 10,
dexterity = item.parseInt(DEXTERITY) ?: 10,
constitution = item.parseInt(CONSTITUTION) ?: 10,
intelligence = item.parseInt(INTELLIGENCE) ?: 10,
wisdom = item.parseInt(WISDOM) ?: 10,
charisma = item.parseInt(CHARISMA) ?: 10,
strengthSavingThrows = item.parseInt(STRENGTH_SAVING_THROW) ?: 0,
dexteritySavingThrows = item.parseInt(DEXTERITY_SAVING_THROW) ?: 0,
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW) ?: 0,
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW) ?: 0,
wisdomSavingThrows = item.parseInt(WISDOM_SAVING_THROW) ?: 0,
charismaSavingThrows = item.parseInt(CHARISMA_SAVING_THROW) ?: 0,
acrobatics = item.parseInt(ACROBATICS) ?: 0,
animalHandling = item.parseInt(ANIMAL_HANDLING) ?: 0,
arcana = item.parseInt(ARCANA) ?: 0,
athletics = item.parseInt(ATHLETICS) ?: 0,
deception = item.parseInt(DECEPTION) ?: 0,
history = item.parseInt(HISTORY) ?: 0,
insight = item.parseInt(INSIGHT) ?: 0,
intimidation = item.parseInt(INTIMIDATION) ?: 0,
investigation = item.parseInt(INVESTIGATION) ?: 0,
medicine = item.parseInt(MEDICINE) ?: 0,
nature = item.parseInt(NATURE) ?: 0,
perception = item.parseInt(PERCEPTION) ?: 0,
performance = item.parseInt(PERFORMANCE) ?: 0,
persuasion = item.parseInt(PERSUASION) ?: 0,
religion = item.parseInt(RELIGION) ?: 0,
sleightOfHand = item.parseInt(SLEIGHT_OF_HAND) ?: 0,
stealth = item.parseInt(STEALTH) ?: 0,
survival = item.parseInt(SURVIVAL) ?: 0,
)
} else {
null

View file

@ -0,0 +1,66 @@
package com.pixelized.rplexicon.ui.screens.character
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.screens.character.composable.Action
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
import com.pixelized.rplexicon.ui.screens.character.composable.Counter
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
@Composable
fun ActionsPages(
modifier: Modifier = Modifier,
actions: State<List<ActionsUio>>,
counter: State<List<CounterUio>>,
alterations: State<List<String>>,
onHit: (id: String) -> Unit,
onDamage: (id: String) -> Unit,
) {
Column(
modifier = modifier,
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
counter.value.forEach {
Counter(
counter = it,
)
}
actions.value.forEach {
Action(
action = it,
onHit = onHit,
onDamage = onDamage,
)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
alterations.value.forEach {
Text(
text = it
)
}
}
}
}

View file

@ -2,19 +2,21 @@ package com.pixelized.rplexicon.ui.screens.character
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
@ -25,7 +27,6 @@ import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@ -40,13 +41,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@ -55,16 +51,13 @@ import com.pixelized.rplexicon.NO_WINDOW_INSETS
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.Loader
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.screens.character.composable.Action
import com.pixelized.rplexicon.ui.screens.character.composable.ActionsUio
import com.pixelized.rplexicon.ui.screens.character.composable.Counter
import com.pixelized.rplexicon.ui.screens.character.composable.CounterUio
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPoint
import com.pixelized.rplexicon.ui.screens.character.composable.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.StatUio
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
import kotlinx.coroutines.launch
@Stable
@ -78,7 +71,7 @@ data class CharacterSheetUio(
val proficiencies: List<ProficiencyUio>,
)
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun CharacterSheetScreen(
viewModel: CharacterSheetViewModel = hiltViewModel(),
@ -92,7 +85,7 @@ fun CharacterSheetScreen(
onRefresh = { scope.launch { viewModel.update() } },
)
Box(
Surface(
modifier = Modifier.fillMaxSize(),
) {
viewModel.sheet.value?.let {
@ -101,8 +94,15 @@ fun CharacterSheetScreen(
.fillMaxSize()
.systemBarsPadding(),
state = rememberScrollState(),
refreshState = refresh,
pagerState = rememberPagerState {
if (viewModel.actions.value.isNotEmpty() || viewModel.counter.value.isNotEmpty()) {
2
} else {
1
}
},
refreshing = viewModel.isLoading,
refreshState = refresh,
onRefresh = { scope.launch { viewModel.update() } },
sheet = it,
actions = viewModel.actions,
@ -177,11 +177,16 @@ fun CharacterSheetScreen(
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@OptIn(
ExperimentalMaterial3Api::class,
ExperimentalMaterialApi::class,
ExperimentalFoundationApi::class,
)
@Composable
private fun CharacterSheetContent(
modifier: Modifier = Modifier,
state: ScrollState,
pagerState: PagerState,
refreshState: PullRefreshState,
refreshing: State<Boolean>,
onRefresh: () -> Unit,
@ -202,7 +207,6 @@ private fun CharacterSheetContent(
contentWindowInsets = NO_WINDOW_INSETS,
topBar = {
TopAppBar(
modifier = Modifier.shadow(elevation = 4.dp),
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
@ -228,110 +232,55 @@ private fun CharacterSheetContent(
},
) { paddingValues ->
Surface(
modifier = Modifier
.pullRefresh(refreshState)
.verticalScroll(state = state)
.padding(paddingValues = paddingValues),
modifier = Modifier.padding(paddingValues = paddingValues),
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(
space = 16.dp,
alignment = Alignment.CenterHorizontally
),
Column {
Surface(
modifier = Modifier.shadow(elevation = 4.dp),
) {
LabelPoint(label = sheet.armorClass)
LabelPoint(label = sheet.hitPoint)
LabelPoint(label = sheet.speed)
}
ProficiencyPage(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
sheet = sheet,
onInitiative = onInitiative,
onStats = onStats,
onProficiencies = onProficiencies,
)
if (actions.value.isNotEmpty() || counter.value.isNotEmpty()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(
space = 16.dp,
alignment = Alignment.CenterHorizontally
),
) {
Image(
modifier = Modifier.graphicsLayer { rotationY = 180f },
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
Text(
modifier = Modifier
.weight(weight = 1f, fill = false)
.padding(horizontal = 8.dp),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(R.string.character_sheet_title_actions),
)
Image(
painter = painterResource(id = R.drawable.art_clip_1),
contentScale = ContentScale.FillWidth,
alignment = Alignment.Center,
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
contentDescription = null,
)
}
Column(
modifier = Modifier.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
counter.value.forEach {
Counter(
counter = it,
)
}
actions.value.forEach {
Action(
action = it,
onHit = onHit,
onDamage = onDamage,
)
}
LabelPoint(label = sheet.armorClass)
LabelPoint(label = sheet.hitPoint)
LabelPoint(label = sheet.speed)
}
}
if (alterations.value.isNotEmpty()) {
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.padding(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
alterations.value.forEach {
Text(
text = it
)
}
HorizontalPager(
modifier = Modifier
.fillMaxWidth()
.pullRefresh(refreshState)
.verticalScroll(state = state),
state = pagerState,
contentPadding = PaddingValues(all = 16.dp),
pageSpacing = 8.dp,
verticalAlignment = Alignment.Top,
) {
when (it) {
0 -> ProficiencyPage(
modifier = Modifier.fillMaxWidth(),
sheet = sheet,
onInitiative = onInitiative,
onStats = onStats,
onProficiencies = onProficiencies,
)
1 -> ActionsPages(
modifier = Modifier.fillMaxWidth(),
actions = actions,
counter = counter,
alterations = alterations,
onHit = onHit,
onDamage = onDamage,
)
}
}
}
@ -351,10 +300,10 @@ private fun CharacterSheetContent(
}
}
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, heightDp = 2000)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, heightDp = 2000)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun CharacterScreenPreview() {
LexiconTheme {
val bru = remember {
@ -540,6 +489,7 @@ private fun CharacterScreenPreview() {
CharacterSheetContent(
modifier = Modifier.fillMaxSize(),
state = rememberScrollState(),
pagerState = rememberPagerState { 1 },
refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }),
refreshing = remember { mutableStateOf(false) },
onRefresh = { },

View file

@ -108,7 +108,6 @@ private fun SearchScreenContent(
containerColor = Color.Transparent,
topBar = {
TopAppBar(
modifier = Modifier.shadow(elevation = 4.dp),
windowInsets = NO_WINDOW_INSETS,
navigationIcon = {
IconButton(onClick = onBack) {
@ -128,7 +127,7 @@ private fun SearchScreenContent(
modifier = Modifier.padding(paddingValues = paddingValues),
header = {
SearchBox(
modifier = Modifier.padding(horizontal = 16.dp),
modifier = Modifier.shadow(elevation = 4.dp),
form = form,
)
},
@ -161,27 +160,36 @@ private fun SearchBox(
modifier: Modifier = Modifier,
form: SearchFormUio,
) {
Column(
Surface(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
TextField(
modifier = Modifier.fillMaxWidth(),
field = form.search,
)
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
DropDownField(
modifier = Modifier.weight(1f),
field = form.gender,
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
field = form.search,
)
DropDownField(
modifier = Modifier.weight(1f),
field = form.race,
Row(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
DropDownField(
modifier = Modifier.weight(weight = 1f, fill = true),
field = form.gender,
)
DropDownField(
modifier = Modifier.weight(weight = 1f, fill = true),
field = form.race,
)
}
Divider(
modifier = Modifier.padding(top = 16.dp),
color = MaterialTheme.lexicon.colorScheme.placeholder,
)
}
Divider(modifier = Modifier.padding(top = 16.dp, bottom = 8.dp))
}
}