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? {
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? {
return when (this.name) {
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.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalRollOverlay
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.ProficiencyViewModel
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexiconShadow
import kotlinx.coroutines.launch
@OptIn(
@ -226,7 +228,6 @@ private fun CharacterSheetContent(
contentWindowInsets = NO_WINDOW_INSETS,
topBar = {
TopAppBar(
modifier = Modifier.shadow(elevation = 4.dp),
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
@ -278,6 +279,9 @@ private fun CharacterSheetContent(
) {
Column {
PagerHeader(
modifier = Modifier
.zIndex(1f)
.lexiconShadow(),
pagerState = pagerState,
onTab = onTab,
)
@ -310,12 +314,15 @@ private fun CharacterSheetContent(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun PagerHeader(
modifier: Modifier = Modifier,
pagerState: PagerState,
tabs: List<String> = headers(),
onTab: (Int) -> Unit,
) {
ScrollableTabRow(
modifier = modifier,
selectedTabIndex = pagerState.currentPage,
divider = { },
) {
tabs.forEachIndexed { index, tab ->
Tab(
@ -364,7 +371,7 @@ private fun CharacterScreenPreview(
CharacterSheetContent(
modifier = Modifier.fillMaxSize(),
sheetState = sheetState,
pagerState = rememberPagerState(initialPage = preview) { 2 },
pagerState = rememberPagerState(initialPage = preview) { 4 },
refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }),
name = "Brulkhai",
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_YES
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.IntrinsicSize
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.width
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
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 com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.rememberTextSize
@Stable
data class InventoryItemUio(
val name: String,
val amount: String? = null,
val items: List<InventoryItemUio> = emptyList(),
)
@OptIn(ExperimentalLayoutApi::class)
@ -33,30 +42,63 @@ fun InventoryItem(
padding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 2.dp),
item: InventoryItemUio,
) {
FlowRow(
Column(
modifier = Modifier
.padding(paddingValues = padding)
.then(other = modifier),
) {
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyMedium,
text = item.name,
)
item.amount?.let {
FlowRow {
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Medium,
style = MaterialTheme.typography.labelLarge,
text = " : ",
)
Text(
modifier = Modifier.alignByBaseline(),
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
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
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun InventoryItemPreview(
@PreviewParameter(InventoryItemPreviewProvider::class) preview: InventoryItemUio,
) {
private fun InventoryItemPreview() {
LexiconTheme {
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.mutableStateOf
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
@Composable
@ -15,18 +13,26 @@ fun rememberInventoryListState(): State<List<InventoryItemUio>> {
return remember {
mutableStateOf(
listOf(
InventoryItemUio(name = "Bourse"),
InventoryItemUio(name = "- Or", amount = "21"),
InventoryItemUio(name = "Sac à dos"),
InventoryItemUio(name = "- Sac de couchage"),
InventoryItemUio(name = "- Kit de cuisine"),
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 = "Bourse",
items = listOf(
InventoryItemUio(name = "Or", amount = "21"),
),
),
InventoryItemUio(
name = "Sac à dos",
items = listOf(
InventoryItemUio(name = "Sac de couchage"),
InventoryItemUio(name = "Kit de cuisine"),
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 = "Javelot", amount = "4"),
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.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -94,12 +95,6 @@ private fun InventoryPageContent(
}
items(items = inventory.value) {
InventoryItem(
modifier = Modifier.padding(
top = when (it.name.contains("-")) {
true -> 0.dp
else -> 4.dp
}
),
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.screens.character.composable.actions.EquipmentItemUio
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.utilitary.extentions.context
import dagger.hilt.android.lifecycle.HiltViewModel
@ -28,6 +29,7 @@ class InventoryViewModel @Inject constructor(
private val inventoryRepository: InventoryRepository,
private val equipmentRepository: EquipmentRepository,
private val descriptionRepository: DescriptionRepository,
private val itemFactory: ItemUioFactory,
savedStateHandle: SavedStateHandle,
application: Application
) : AndroidViewModel(application) {
@ -48,15 +50,10 @@ class InventoryViewModel @Inject constructor(
init {
viewModelScope.launch {
launch(Dispatchers.IO) {
inventoryRepository.data.collect {
val items = it[character]?.items?.map { item ->
InventoryItemUio(
name = item.name,
amount = item.amount,
)
} ?: emptyList()
inventoryRepository.data.collect { inventories ->
val items = inventories[character]?.items?.map { itemFactory.toUio(it) }
withContext(Dispatchers.Main) {
_inventory.value = items
_inventory.value = items ?: emptyList()
}
}
}
@ -88,7 +85,7 @@ class InventoryViewModel @Inject constructor(
}
}
}
fun showSkillDetailDialog(item: String) {
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.Transition
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateOffsetAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
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.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
@ -31,6 +29,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
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.graphics.Color
import androidx.compose.ui.graphics.Shape
@ -151,4 +152,23 @@ fun Modifier.ddBorder(
.clip(
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)
}
}