From 898a8862db69d7a04a2f59de13033a51c5cf12c3 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 6 May 2025 14:25:39 +0200 Subject: [PATCH] Change the UI of the InventoryItem. --- .../inventory/CharacterDetailInventory.kt | 7 ++ .../CharacterDetailInventoryFactory.kt | 3 + .../detail/inventory/item/InventoryItem.kt | 117 ++++++++++++------ .../desktop/lwa/ui/theme/LwaTheme.kt | 4 +- .../lwa/ui/theme/typography/LwaTypography.kt | 31 +++++ .../PaddingValues+calculatePaddings.kt | 34 +++++ .../lwa/utils/rememberBackgroundGradient.kt | 19 +-- 7 files changed, 169 insertions(+), 46 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/PaddingValues+calculatePaddings.kt diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventory.kt index 1a2320d..cb57273 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventory.kt @@ -32,6 +32,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -95,6 +96,7 @@ fun CharacterDetailInventory( inventory: State, ) { val blur = LocalBlurController.current + val focus = LocalFocusManager.current val scope = rememberCoroutineScope() when (val unWrap = inventory.value) { @@ -110,6 +112,7 @@ fun CharacterDetailInventory( purseViewModel.showPurseDialog( characterSheetId = it, ) + focus.clearFocus(force = true) }, onItem = { item -> blur.show() @@ -118,12 +121,14 @@ fun CharacterDetailInventory( inventoryId = item.inventoryId, itemId = item.itemId, ) + focus.clearFocus(force = true) }, onAddItem = { blur.show() inventoryDialogViewModel.showInventoryDialog( characterSheetId = it, ) + focus.clearFocus(force = true) }, onConsume = { scope.launch { @@ -132,6 +137,7 @@ fun CharacterDetailInventory( inventoryId = it.inventoryId, ) } + focus.clearFocus(force = true) }, onEquip = { scope.launch { @@ -140,6 +146,7 @@ fun CharacterDetailInventory( inventoryId = it.inventoryId, ) } + focus.clearFocus(force = true) } ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventoryFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventoryFactory.kt index ac22b8a..0f429a3 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventoryFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/CharacterDetailInventoryFactory.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.stateIn import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.character__inventory__filter_inventory__label import org.jetbrains.compose.resources.getString +import org.koin.core.component.getScopeName import java.text.Collator import java.text.DecimalFormat @@ -96,6 +97,7 @@ class CharacterDetailInventoryFactory( characterSheetId = characterSheetId, inventoryId = it.inventoryId, itemId = it.itemId, + icon = item.metadata.thumbnail, label = item.metadata.label, count = decimalFormat.format(it.count), equipped = it.equipped, @@ -111,6 +113,7 @@ class CharacterDetailInventoryFactory( ) } ?.sortedWith(compareBy(Collator.getInstance()) { it.label }) + ?.sortedByDescending { it.equipped } ?: emptyList() ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/item/InventoryItem.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/item/InventoryItem.kt index a5ed29c..556fc0b 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/item/InventoryItem.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/detail/inventory/item/InventoryItem.kt @@ -4,8 +4,8 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.SizeTransform import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.animation.togetherWith import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -15,11 +15,14 @@ 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.calculateEndPadding +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text @@ -27,22 +30,24 @@ import androidx.compose.material.TextButton import androidx.compose.material.minimumInteractiveComponentSize import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox import com.pixelized.desktop.lwa.ui.composable.image.DesaturatedAsyncImage import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout2 import com.pixelized.desktop.lwa.ui.theme.lwa +import com.pixelized.desktop.lwa.utils.extention.calculatePaddings import com.pixelized.desktop.lwa.utils.extention.ribbon import com.pixelized.desktop.lwa.utils.rememberSaturationFilter import lwacharactersheet.composeapp.generated.resources.Res @@ -56,6 +61,7 @@ data class InventoryItemUio( val characterSheetId: String, val inventoryId: String, val itemId: String, + val icon: String?, val label: String, val count: String, val equipped: Boolean, @@ -74,11 +80,13 @@ data class InventoryItemUio( @Stable object GMCharacterPreviewDefault { @Stable - val paddings = PaddingValues(horizontal = 16.dp) + val paddings = PaddingValues(horizontal = 8.dp) @Stable val toolTipPaddings = PaddingValues(all = 16.dp) + val icon: DpSize = DpSize(width = 32.dp, height = 32.dp) + @Stable val spacing: Dp = 8.dp } @@ -90,17 +98,14 @@ fun InventoryItem( padding: PaddingValues = GMCharacterPreviewDefault.paddings, toolTipPaddings: PaddingValues = GMCharacterPreviewDefault.toolTipPaddings, spacing: Dp = GMCharacterPreviewDefault.spacing, + icon: DpSize = GMCharacterPreviewDefault.icon, item: InventoryItemUio, onClick: () -> Unit, onConsume: () -> Unit, onEquip: () -> Unit, ) { - val layoutDirection = LocalLayoutDirection.current - val toolTop = remember(toolTipPaddings) { toolTipPaddings.calculateTopPadding() } - val toolEnd = remember(toolTipPaddings, layoutDirection) { - toolTipPaddings.calculateEndPadding(layoutDirection) - } - val end = remember(padding, layoutDirection) { padding.calculateEndPadding(layoutDirection) } + val (start, top, end, bottom) = padding.calculatePaddings() + val (_, toolTop, toolEnd, _) = toolTipPaddings.calculatePaddings() TooltipLayout2( modifier = modifier, @@ -159,44 +164,84 @@ fun InventoryItem( ) .minimumInteractiveComponentSize() .fillMaxWidth() - .padding(paddingValues = padding) + .padding(start = start, end = end) .then(other = modifier), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { Row( modifier = Modifier.weight(weight = 1f), - horizontalArrangement = Arrangement.spacedBy(space = spacing / 2), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = spacing), ) { + Box( + modifier = Modifier + .height(height = icon.height) + .widthIn(min = icon.width, max = icon.width * 3) + .graphicsLayer { clip = false }, + contentAlignment = Alignment.CenterStart, + ) { + Box( + modifier = Modifier + .size(size = icon) + .background( + color = MaterialTheme.lwa.colorScheme.elevated.base4dp, + shape = CircleShape, + ) + ) + AsyncImage( + modifier = Modifier + .size(size = icon) + .aspectRatio(ratio = 1f, matchHeightConstraintsFirst = true), + model = item.icon, + contentScale = ContentScale.Crop, + contentDescription = null, + ) + if (item.consumable) { + AnimatedContent( + modifier = Modifier + .align(alignment = Alignment.BottomEnd) + .offset(y = 4.dp), + targetState = item.count, + transitionSpec = { + val enter = fadeIn() + slideInVertically { -8 } + val exit = fadeOut() + slideOutVertically { 8 } + enter togetherWith exit using SizeTransform(clip = false) + }, + ) { + Box( + contentAlignment = Alignment.Center, + ) { + Text( + style = MaterialTheme.lwa.typography.inventory.countOutline, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = it, + ) + Text( + style = MaterialTheme.lwa.typography.inventory.countInline, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = it, + ) + } + } + } + } Text( - modifier = Modifier.alignByBaseline().weight(weight = 1f, fill = false), - style = MaterialTheme.lwa.typography.base.body1, - fontWeight = FontWeight.Bold, + modifier = Modifier + .padding(top = top, bottom = bottom) + .weight(weight = 1f, fill = false), + style = MaterialTheme.lwa.typography.inventory.label, overflow = TextOverflow.Ellipsis, maxLines = 1, text = item.label, ) - if (item.consumable) { - AnimatedContent( - modifier = Modifier.alignByBaseline(), - targetState = item.count, - transitionSpec = { - val enter = fadeIn() + slideInHorizontally { 16 } - val exit = fadeOut() + slideOutHorizontally { -16 } - enter togetherWith exit using SizeTransform(clip = false) - }, - ) { - Text( - style = MaterialTheme.lwa.typography.base.caption, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - text = it, - ) - } - } } Row( - modifier = Modifier.offset(x = end - spacing), + modifier = Modifier + .padding(top = top, bottom = bottom) + .offset(x = end - spacing), horizontalArrangement = Arrangement.spacedBy(space = spacing), ) { if (item.consumable) { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt index 3e06b69..63514a6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt @@ -3,6 +3,7 @@ package com.pixelized.desktop.lwa.ui.theme import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.Stable import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.remember @@ -20,8 +21,9 @@ val LocalLwaTheme = compositionLocalOf { } val MaterialTheme.lwa: LwaTheme - @Composable @Stable + @Composable + @ReadOnlyComposable get() = LocalLwaTheme.current @Stable diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt index 20ea5cc..5ebbabc 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt @@ -7,6 +7,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.drawscope.Fill +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontStyle @@ -15,6 +18,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp import com.pixelized.desktop.lwa.ui.theme.color.LwaColors import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography.Settings +import com.pixelized.shared.lwa.model.inventory.Inventory import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.consola_mono_bold import lwacharactersheet.composeapp.generated.resources.consola_mono_book @@ -26,6 +30,7 @@ data class LwaTypography( val chat: Chat, val settings: Settings, val portrait: Portrait, + val inventory: Inventory, ) { @Stable data class Chat( @@ -46,6 +51,13 @@ data class LwaTypography( val value: TextStyle, val max: TextStyle, ) + + @Stable + data class Inventory( + val countOutline: TextStyle, + val countInline: TextStyle, + val label: TextStyle, + ) } @Composable @@ -118,6 +130,25 @@ fun lwaTypography( blurRadius = 2f, ), ), + ), + inventory = LwaTypography.Inventory( + countOutline = base.caption.copy( + fontWeight = FontWeight.SemiBold, + color = colors.elevated.base4dp, + drawStyle = Stroke( + miter = 3f, + width = 3f, + join = StrokeJoin.Round, + ) + ), + countInline = base.caption.copy( + fontWeight = FontWeight.SemiBold, + color = colors.base.onSurface, + drawStyle = Fill + ), + label = base.body1.copy( + fontWeight = FontWeight.Normal, + ), ) ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/PaddingValues+calculatePaddings.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/PaddingValues+calculatePaddings.kt new file mode 100644 index 0000000..09078f4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/PaddingValues+calculatePaddings.kt @@ -0,0 +1,34 @@ +package com.pixelized.desktop.lwa.utils.extention + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.Stable +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection + +@Composable +@ReadOnlyComposable +fun PaddingValues.calculatePaddings( + layoutDirection: LayoutDirection = LocalLayoutDirection.current, +): ComputedPaddingValues { + return ComputedPaddingValues( + start = calculateStartPadding(layoutDirection = layoutDirection), + top = calculateTopPadding(), + end = calculateEndPadding(layoutDirection = layoutDirection), + bottom = calculateBottomPadding(), + ) +} + +@Immutable +@Stable +data class ComputedPaddingValues( + @Stable val start: Dp, + @Stable val top: Dp, + @Stable val end: Dp, + @Stable val bottom: Dp, +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/rememberBackgroundGradient.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/rememberBackgroundGradient.kt index a33468e..f0c543c 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/rememberBackgroundGradient.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/rememberBackgroundGradient.kt @@ -2,21 +2,22 @@ package com.pixelized.desktop.lwa.utils import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import com.pixelized.desktop.lwa.ui.theme.lwa @Composable +@ReadOnlyComposable fun rememberBackgroundGradient( + color: Color = MaterialTheme.lwa.colorScheme.base.surface, from: Float = 0.5f, to: Float = 1.0f, ): Brush { - val colorScheme = MaterialTheme.colors - return remember(colorScheme) { - Brush.verticalGradient( - colors = listOf( - colorScheme.surface.copy(alpha = from), - colorScheme.surface.copy(alpha = to), - ) + return Brush.verticalGradient( + colors = listOf( + color.copy(alpha = from), + color.copy(alpha = to), ) - } + ) } \ No newline at end of file