Add Tree like structure to the inventory.

This commit is contained in:
Thomas Andres Gomez 2023-10-23 18:42:20 +02:00
parent daea5fd6dc
commit a55e2c8162
8 changed files with 158 additions and 72 deletions

View file

@ -11,7 +11,7 @@ data class Inventory(
) )
fun find(container: String): Item.Builder? { fun find(container: String): Item.Builder? {
return items.firstOrNull { it.find(name = container) != null } return items.localFind { it.find(name = container) }
} }
} }
@ -34,9 +34,20 @@ data class Inventory(
fun find(name: String): Builder? { fun find(name: String): Builder? {
return when (this.name) { return when (this.name) {
name -> this name -> this
else -> items.firstOrNull { it.find(name = name) != null } else -> items.localFind { it.find(name = name) }
} }
} }
} }
} }
}
private inline fun <T> Iterable<T>.localFind(predicate: (T) -> T?): T? {
var single: T? = null
for (element in this) {
single = predicate(element)
if (single != null) {
break
}
}
return single
} }

View file

@ -56,6 +56,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalRollOverlay import com.pixelized.rplexicon.LocalRollOverlay
import com.pixelized.rplexicon.LocalSnack import com.pixelized.rplexicon.LocalSnack
@ -81,6 +82,7 @@ import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.Proficienc
import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyPreview import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyPreview
import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyViewModel import com.pixelized.rplexicon.ui.screens.character.pages.proficiency.ProficiencyViewModel
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexiconShadow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn( @OptIn(
@ -226,7 +228,6 @@ private fun CharacterSheetContent(
contentWindowInsets = NO_WINDOW_INSETS, contentWindowInsets = NO_WINDOW_INSETS,
topBar = { topBar = {
TopAppBar( TopAppBar(
modifier = Modifier.shadow(elevation = 4.dp),
navigationIcon = { navigationIcon = {
IconButton(onClick = onBack) { IconButton(onClick = onBack) {
Icon( Icon(
@ -278,6 +279,9 @@ private fun CharacterSheetContent(
) { ) {
Column { Column {
PagerHeader( PagerHeader(
modifier = Modifier
.zIndex(1f)
.lexiconShadow(),
pagerState = pagerState, pagerState = pagerState,
onTab = onTab, onTab = onTab,
) )
@ -310,12 +314,15 @@ private fun CharacterSheetContent(
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun PagerHeader( private fun PagerHeader(
modifier: Modifier = Modifier,
pagerState: PagerState, pagerState: PagerState,
tabs: List<String> = headers(), tabs: List<String> = headers(),
onTab: (Int) -> Unit, onTab: (Int) -> Unit,
) { ) {
ScrollableTabRow( ScrollableTabRow(
modifier = modifier,
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
divider = { },
) { ) {
tabs.forEachIndexed { index, tab -> tabs.forEachIndexed { index, tab ->
Tab( Tab(
@ -364,7 +371,7 @@ private fun CharacterScreenPreview(
CharacterSheetContent( CharacterSheetContent(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
sheetState = sheetState, sheetState = sheetState,
pagerState = rememberPagerState(initialPage = preview) { 2 }, pagerState = rememberPagerState(initialPage = preview) { 4 },
refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }), refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }),
name = "Brulkhai", name = "Brulkhai",
onBack = { }, onBack = { },

View file

@ -2,28 +2,37 @@ package com.pixelized.rplexicon.ui.screens.character.composable.actions
import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.rememberTextSize
@Stable @Stable
data class InventoryItemUio( data class InventoryItemUio(
val name: String, val name: String,
val amount: String? = null, val amount: String? = null,
val items: List<InventoryItemUio> = emptyList(),
) )
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@ -33,30 +42,63 @@ fun InventoryItem(
padding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 2.dp), padding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 2.dp),
item: InventoryItemUio, item: InventoryItemUio,
) { ) {
FlowRow( Column(
modifier = Modifier modifier = Modifier
.padding(paddingValues = padding) .padding(paddingValues = padding)
.then(other = modifier), .then(other = modifier),
) { ) {
Text( FlowRow {
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyMedium,
text = item.name,
)
item.amount?.let {
Text( Text(
modifier = Modifier.alignByBaseline(), modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.labelLarge,
text = " : ",
)
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Light,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
text = it text = item.name,
) )
item.amount?.let {
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Medium,
style = MaterialTheme.typography.labelLarge,
text = " : ",
)
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Light,
style = MaterialTheme.typography.bodyMedium,
text = it
)
}
}
val lastIndex = remember(item.items.size) { item.items.lastIndex }
item.items.forEachIndexed { index, item ->
Row(
modifier = Modifier.height(intrinsicSize = IntrinsicSize.Min),
) {
val size = rememberTextSize(style = MaterialTheme.typography.bodyMedium)
if (index == lastIndex) {
Divider(
modifier = Modifier
.height(height = 3.dp + size.height / 2)
.width(1.dp)
)
} else {
Divider(
modifier = Modifier
.fillMaxHeight()
.width(1.dp)
)
}
Divider(
modifier = Modifier
.padding(top = 2.dp + size.height / 2)
.height(1.dp)
.width(8.dp)
)
InventoryItem(
padding = PaddingValues(start = 7.dp, top = 2.dp, bottom = 2.dp),
item = item,
)
}
} }
} }
} }
@ -64,25 +106,17 @@ fun InventoryItem(
@Composable @Composable
@Preview(uiMode = UI_MODE_NIGHT_NO) @Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES) @Preview(uiMode = UI_MODE_NIGHT_YES)
private fun InventoryItemPreview( private fun InventoryItemPreview() {
@PreviewParameter(InventoryItemPreviewProvider::class) preview: InventoryItemUio,
) {
LexiconTheme { LexiconTheme {
Surface { Surface {
InventoryItem(item = preview) Column {
val items = rememberInventoryListState()
items.value.forEach {
InventoryItem(
item = it,
)
}
}
} }
} }
}
private class InventoryItemPreviewProvider : PreviewParameterProvider<InventoryItemUio> {
override val values: Sequence<InventoryItemUio> = sequenceOf(
InventoryItemUio(
name = "- Cordes",
amount = "15 mètres",
),
InventoryItemUio(
name = "- Piège de chasse",
amount = null,
)
)
} }

View file

@ -5,8 +5,6 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
@Composable @Composable
@ -15,18 +13,26 @@ fun rememberInventoryListState(): State<List<InventoryItemUio>> {
return remember { return remember {
mutableStateOf( mutableStateOf(
listOf( listOf(
InventoryItemUio(name = "Bourse"), InventoryItemUio(
InventoryItemUio(name = "- Or", amount = "21"), name = "Bourse",
InventoryItemUio(name = "Sac à dos"), items = listOf(
InventoryItemUio(name = "- Sac de couchage"), InventoryItemUio(name = "Or", amount = "21"),
InventoryItemUio(name = "- Kit de cuisine"), ),
InventoryItemUio(name = "- Boite d'allume-feu"), ),
InventoryItemUio(name = "- Torches", amount = "10"), InventoryItemUio(
InventoryItemUio(name = "- Rations journalières", amount = "10"), name = "Sac à dos",
InventoryItemUio(name = "- Outre d'eau"), items = listOf(
InventoryItemUio(name = "- Cordes", amount = "15 mètres"), InventoryItemUio(name = "Sac de couchage"),
InventoryItemUio(name = "- Piège de chasse"), InventoryItemUio(name = "Kit de cuisine"),
InventoryItemUio(name = "- Bâton de marche"), InventoryItemUio(name = "Boite d'allume-feu"),
InventoryItemUio(name = "Torches", amount = "10"),
InventoryItemUio(name = "Rations journalières", amount = "10"),
InventoryItemUio(name = "Outre d'eau"),
InventoryItemUio(name = "Cordes", amount = "15 mètres"),
InventoryItemUio(name = "Piège de chasse"),
InventoryItemUio(name = "Bâton de marche"),
),
),
InventoryItemUio(name = "Dague"), InventoryItemUio(name = "Dague"),
InventoryItemUio(name = "Javelot", amount = "4"), InventoryItemUio(name = "Javelot", amount = "4"),
InventoryItemUio(name = "Cape de protection"), InventoryItemUio(name = "Cape de protection"),

View file

@ -0,0 +1,16 @@
package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import javax.inject.Inject
class ItemUioFactory @Inject constructor() {
fun toUio(item: Inventory.Item): InventoryItemUio {
return InventoryItemUio(
name = item.name,
amount = item.amount,
items = item.items.map { toUio(it) }
)
}
}

View file

@ -5,6 +5,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@ -94,12 +95,6 @@ private fun InventoryPageContent(
} }
items(items = inventory.value) { items(items = inventory.value) {
InventoryItem( InventoryItem(
modifier = Modifier.padding(
top = when (it.name.contains("-")) {
true -> 0.dp
else -> 4.dp
}
),
item = it, item = it,
) )
} }

View file

@ -13,6 +13,7 @@ import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import com.pixelized.rplexicon.ui.screens.character.factory.ItemUioFactory
import com.pixelized.rplexicon.ui.screens.character.pages.actions.SkillDetailUio import com.pixelized.rplexicon.ui.screens.character.pages.actions.SkillDetailUio
import com.pixelized.rplexicon.utilitary.extentions.context import com.pixelized.rplexicon.utilitary.extentions.context
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -28,6 +29,7 @@ class InventoryViewModel @Inject constructor(
private val inventoryRepository: InventoryRepository, private val inventoryRepository: InventoryRepository,
private val equipmentRepository: EquipmentRepository, private val equipmentRepository: EquipmentRepository,
private val descriptionRepository: DescriptionRepository, private val descriptionRepository: DescriptionRepository,
private val itemFactory: ItemUioFactory,
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
application: Application application: Application
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
@ -48,15 +50,10 @@ class InventoryViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
inventoryRepository.data.collect { inventoryRepository.data.collect { inventories ->
val items = it[character]?.items?.map { item -> val items = inventories[character]?.items?.map { itemFactory.toUio(it) }
InventoryItemUio(
name = item.name,
amount = item.amount,
)
} ?: emptyList()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
_inventory.value = items _inventory.value = items ?: emptyList()
} }
} }
} }
@ -88,7 +85,7 @@ class InventoryViewModel @Inject constructor(
} }
} }
} }
fun showSkillDetailDialog(item: String) { fun showSkillDetailDialog(item: String) {
val description = descriptionRepository.find(name = item) val description = descriptionRepository.find(name = item)

View file

@ -2,14 +2,11 @@ package com.pixelized.rplexicon.utilitary.extentions
import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.Transition import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateOffsetAsState
import androidx.compose.animation.core.spring import androidx.compose.animation.core.spring
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.detectTransformGestures import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
@ -18,6 +15,7 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -31,6 +29,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
@ -151,4 +152,23 @@ fun Modifier.ddBorder(
.clip( .clip(
shape = inner, shape = inner,
) )
}
fun Modifier.lexiconShadow() = composed {
if (isSystemInDarkTheme()) {
val color = DividerDefaults.color
drawWithContent {
drawContent()
val thickness = DividerDefaults.Thickness.toPx()
val y = size.height
drawLine(
color = color,
start = Offset(0f, y),
end = Offset(size.width, y),
strokeWidth = thickness
)
}
} else {
shadow(elevation = 4.dp)
}
} }