This commit was merged in pull request #58.
This commit is contained in:
@@ -27,6 +27,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
|
|||||||
import org.shahondin1624.lib.components.TestTags
|
import org.shahondin1624.lib.components.TestTags
|
||||||
import org.shahondin1624.lib.components.UiConstants
|
import org.shahondin1624.lib.components.UiConstants
|
||||||
import org.shahondin1624.lib.components.charactermodel.CharacterHeader
|
import org.shahondin1624.lib.components.charactermodel.CharacterHeader
|
||||||
|
import org.shahondin1624.lib.components.charactermodel.DamageMonitorPanel
|
||||||
import org.shahondin1624.lib.components.charactermodel.DerivedAttributesPanel
|
import org.shahondin1624.lib.components.charactermodel.DerivedAttributesPanel
|
||||||
import org.shahondin1624.lib.components.charactermodel.ResourcePanel
|
import org.shahondin1624.lib.components.charactermodel.ResourcePanel
|
||||||
import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage
|
import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage
|
||||||
@@ -314,6 +315,8 @@ private fun MainScaffold(
|
|||||||
Spacer(Modifier.height(contentPadding))
|
Spacer(Modifier.height(contentPadding))
|
||||||
DerivedAttributesPanel(EXAMPLE_CHARACTER.attributes)
|
DerivedAttributesPanel(EXAMPLE_CHARACTER.attributes)
|
||||||
Spacer(Modifier.height(contentPadding))
|
Spacer(Modifier.height(contentPadding))
|
||||||
|
DamageMonitorPanel(EXAMPLE_CHARACTER.damageMonitor)
|
||||||
|
Spacer(Modifier.height(contentPadding))
|
||||||
AttributesPage(EXAMPLE_CHARACTER)
|
AttributesPage(EXAMPLE_CHARACTER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package org.shahondin1624.lib.components.charactermodel
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
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.sp
|
||||||
|
import org.shahondin1624.lib.components.TestTags
|
||||||
|
import org.shahondin1624.lib.components.UiConstants
|
||||||
|
import org.shahondin1624.model.charactermodel.DamageMonitor
|
||||||
|
import org.shahondin1624.theme.LocalWindowSizeClass
|
||||||
|
import org.shahondin1624.theme.WindowSizeClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the damage monitor as visual box grids for stun, physical, and overflow tracks.
|
||||||
|
* Read-only display; interactive damage marking is in story 6.5.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun DamageMonitorPanel(damageMonitor: DamageMonitor) {
|
||||||
|
val windowSizeClass = LocalWindowSizeClass.current
|
||||||
|
val padding = UiConstants.Spacing.medium(windowSizeClass)
|
||||||
|
val spacing = UiConstants.Spacing.small(windowSizeClass)
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(TestTags.PANEL_DAMAGE_MONITOR)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(padding)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Damage Monitor",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(bottom = spacing)
|
||||||
|
)
|
||||||
|
|
||||||
|
when (windowSizeClass) {
|
||||||
|
WindowSizeClass.Compact -> {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(spacing)) {
|
||||||
|
DamageTrack("Stun", damageMonitor.stunCurrent(), damageMonitor.stunMax())
|
||||||
|
DamageTrack("Physical", damageMonitor.physicalCurrent(), damageMonitor.physicalMax())
|
||||||
|
DamageTrack("Overflow", damageMonitor.overflowCurrent(), damageMonitor.overflowMax())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowSizeClass.Medium, WindowSizeClass.Expanded -> {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(spacing)
|
||||||
|
) {
|
||||||
|
DamageTrack("Stun", damageMonitor.stunCurrent(), damageMonitor.stunMax(), Modifier.weight(1f))
|
||||||
|
DamageTrack("Physical", damageMonitor.physicalCurrent(), damageMonitor.physicalMax(), Modifier.weight(1f))
|
||||||
|
DamageTrack("Overflow", damageMonitor.overflowCurrent(), damageMonitor.overflowMax(), Modifier.weight(1f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DamageTrack(
|
||||||
|
label: String,
|
||||||
|
current: Int,
|
||||||
|
max: Int,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val filledColor = MaterialTheme.colorScheme.error
|
||||||
|
val emptyColor = MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
val borderColor = MaterialTheme.colorScheme.outline
|
||||||
|
|
||||||
|
Column(modifier = modifier) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "$current / $max",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.height(4.dp))
|
||||||
|
|
||||||
|
// Box grid with wound modifiers every 3 boxes
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(2.dp)
|
||||||
|
) {
|
||||||
|
for (i in 1..max) {
|
||||||
|
val isFilled = i <= current
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.background(
|
||||||
|
color = if (isFilled) filledColor else emptyColor,
|
||||||
|
shape = MaterialTheme.shapes.extraSmall
|
||||||
|
)
|
||||||
|
.border(
|
||||||
|
width = 1.dp,
|
||||||
|
color = borderColor,
|
||||||
|
shape = MaterialTheme.shapes.extraSmall
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// Show wound modifier after every 3rd box
|
||||||
|
if (i % 3 == 0 && i < max) {
|
||||||
|
val woundLevel = i / 3
|
||||||
|
Text(
|
||||||
|
text = "-$woundLevel",
|
||||||
|
style = MaterialTheme.typography.labelSmall.copy(fontSize = 8.sp),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Empty spacer to maintain alignment
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user