Small UI / UX adjustment on the character sheet detail/edit

This commit is contained in:
Thomas Andres Gomez 2024-11-28 13:36:21 +01:00
parent d1a98b9075
commit 34a0ee13f0
24 changed files with 359 additions and 207 deletions

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<resources> <resources>
<string name="dialog__confirm_action">Confirmer</string>
<string name="dialog__cancel_action">Annuler</string>
<string name="main_page__create_action">Créer une feuille de personnage</string> <string name="main_page__create_action">Créer une feuille de personnage</string>
<string name="main_page__network_action">Configuration de la table</string> <string name="main_page__network_action">Configuration de la table</string>
<string name="main_page__roll_history_action">Consulter l'historique des lancers</string> <string name="main_page__roll_history_action">Consulter l'historique des lancers</string>
@ -19,8 +22,7 @@
<string name="character_sheet_edit__create__title">Création de personnage</string> <string name="character_sheet_edit__create__title">Création de personnage</string>
<string name="character_sheet_edit__edit__title">Édition de personnage</string> <string name="character_sheet_edit__edit__title">Édition de personnage</string>
<string name="character_sheet_edit__name_placeholder">Nom</string> <string name="character_sheet_edit__name_label">Nom</string>
<string name="character_sheet_edit__add_roll_action">Ajouter une action</string>
<string name="character_sheet_edit__save_action">Sauvegarder</string> <string name="character_sheet_edit__save_action">Sauvegarder</string>
<string name="character_sheet_edit__characteristics__title">Caractéristiques</string> <string name="character_sheet_edit__characteristics__title">Caractéristiques</string>
<string name="character_sheet_edit__characteristics__str">Force</string> <string name="character_sheet_edit__characteristics__str">Force</string>
@ -32,7 +34,9 @@
<string name="character_sheet_edit__characteristics__cha">Charisme</string> <string name="character_sheet_edit__characteristics__cha">Charisme</string>
<string name="character_sheet_edit__sub_characteristics__title">Caractéristiques dérivées</string> <string name="character_sheet_edit__sub_characteristics__title">Caractéristiques dérivées</string>
<string name="character_sheet_edit__sub_characteristics__movement">Déplacement</string> <string name="character_sheet_edit__sub_characteristics__movement">Déplacement</string>
<string name="character_sheet_edit__sub_characteristics__max_hit_point">Points de vie maximum</string>
<string name="character_sheet_edit__sub_characteristics__hit_point">Points de vie</string> <string name="character_sheet_edit__sub_characteristics__hit_point">Points de vie</string>
<string name="character_sheet_edit__sub_characteristics__max_power_point">Points de pouvoir maximum</string>
<string name="character_sheet_edit__sub_characteristics__power_point">Points de pouvoir</string> <string name="character_sheet_edit__sub_characteristics__power_point">Points de pouvoir</string>
<string name="character_sheet_edit__sub_characteristics__damage_bonus">Bonus aux dégats</string> <string name="character_sheet_edit__sub_characteristics__damage_bonus">Bonus aux dégats</string>
<string name="character_sheet_edit__sub_characteristics__armor">Armure</string> <string name="character_sheet_edit__sub_characteristics__armor">Armure</string>
@ -64,9 +68,10 @@
<string name="character_sheet_edit__occupation__title">Occupations</string> <string name="character_sheet_edit__occupation__title">Occupations</string>
<string name="character_sheet_edit__occupation__add_action">Ajouter une occupation</string> <string name="character_sheet_edit__occupation__add_action">Ajouter une occupation</string>
<string name="character_sheet_edit__actions__name_label">Nom</string> <string name="character_sheet_edit__actions__name_label">Nom</string>
<string name="character_sheet_edit__actions__action_label">Bonus</string> <string name="character_sheet_edit__actions__action_label">Action de lancé</string>
<string name="character_sheet_edit__magic__title">Compétences magiques</string> <string name="character_sheet_edit__magic__title">Compétences magiques</string>
<string name="character_sheet_edit__magic__add_action">Ajouter une compétence magique</string> <string name="character_sheet_edit__magic__add_action">Ajouter une compétence magique</string>
<string name="character_sheet_edit__add_roll_action">Ajouter une action de lancé</string>
<string name="character_sheet_edit__delete__label">Supprimer</string> <string name="character_sheet_edit__delete__label">Supprimer</string>
<string name="character_sheet_edit__occupation__label">Compétence d'occupation</string> <string name="character_sheet_edit__occupation__label">Compétence d'occupation</string>
@ -91,8 +96,6 @@
<string name="character_sheet__skills__magic_title">Compétences magiques</string> <string name="character_sheet__skills__magic_title">Compétences magiques</string>
<string name="character_sheet__delete_dialog__title">Supprimer la feuille de personnage</string> <string name="character_sheet__delete_dialog__title">Supprimer la feuille de personnage</string>
<string name="character_sheet__delete_dialog__description">Êtes-vous sûr de vouloir supprimer "%1$s" ?</string> <string name="character_sheet__delete_dialog__description">Êtes-vous sûr de vouloir supprimer "%1$s" ?</string>
<string name="character_sheet__delete_dialog__confirm_action">Confirmer</string>
<string name="character_sheet__delete_dialog__cancel_action">Annuler</string>
<string name="tooltip__characteristics__characteristics">Les caractéristiques constituent les aptitudes innées dun personnage comme son intelligence, sa force, son charisme, etc. Elles ne sont pas acquises, mais peuvent être parfois augmentées par un entraînement ou une utilisation réussie. Les caractéristiques des humains normaux varient de 2 (niveau extrêmement bas) à 20 (maximum du potentiel humain), avec une moyenne de 10 ou 11. Plus une caractéristique est élevée plus le personnage est puissant dans cette aptitude.\nÀ la création de votre personnage, répartissez les valeurs suivantes dans les différentes caractéristiques : 15, 15, 13, 11, 10, 9 et 7.</string> <string name="tooltip__characteristics__characteristics">Les caractéristiques constituent les aptitudes innées dun personnage comme son intelligence, sa force, son charisme, etc. Elles ne sont pas acquises, mais peuvent être parfois augmentées par un entraînement ou une utilisation réussie. Les caractéristiques des humains normaux varient de 2 (niveau extrêmement bas) à 20 (maximum du potentiel humain), avec une moyenne de 10 ou 11. Plus une caractéristique est élevée plus le personnage est puissant dans cette aptitude.\nÀ la création de votre personnage, répartissez les valeurs suivantes dans les différentes caractéristiques : 15, 15, 13, 11, 10, 9 et 7.</string>
<string name="tooltip__characteristics__strength">La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais lefficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.</string> <string name="tooltip__characteristics__strength">La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais lefficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.</string>

View file

@ -1,18 +1,21 @@
package com.pixelized.desktop.lwa package com.pixelized.desktop.lwa
import com.pixelized.desktop.lwa.business.DamageBonusUseCase import com.pixelized.desktop.lwa.business.DamageBonusUseCase
import com.pixelized.desktop.lwa.business.RollUseCase
import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase
import com.pixelized.desktop.lwa.business.SkillStepUseCase
import com.pixelized.desktop.lwa.business.SkillValueComputationUseCase import com.pixelized.desktop.lwa.business.SkillValueComputationUseCase
import com.pixelized.desktop.lwa.parser.arithmetic.ArithmeticParser import com.pixelized.desktop.lwa.parser.arithmetic.ArithmeticParser
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetJsonFactory import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetJsonFactory
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetStore
import com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.roll.RollHistoryRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetFactory import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetFactory
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetViewModel import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetViewModel
import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditFactory import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditFactory
import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditViewModel import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditViewModel
import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.screen.main.MainPageViewModel import com.pixelized.desktop.lwa.screen.main.MainPageViewModel
import com.pixelized.desktop.lwa.screen.network.NetworkFactory import com.pixelized.desktop.lwa.screen.network.NetworkFactory
import com.pixelized.desktop.lwa.screen.network.NetworkViewModel import com.pixelized.desktop.lwa.screen.network.NetworkViewModel
@ -67,5 +70,8 @@ val parserDependencies
val useCaseDependencies val useCaseDependencies
get() = module { get() = module {
factoryOf(::DamageBonusUseCase) factoryOf(::DamageBonusUseCase)
factoryOf(::SkillStepUseCase)
factoryOf(::SkillNormalizerUseCase)
factoryOf(::RollUseCase)
factoryOf(::SkillValueComputationUseCase) factoryOf(::SkillValueComputationUseCase)
} }

View file

@ -1,6 +1,6 @@
package com.pixelized.desktop.lwa.business package com.pixelized.desktop.lwa.business
object RollUseCase { class RollUseCase {
fun rollD100(): Int { fun rollD100(): Int {
return roll(quantity = 1, faces = 100) return roll(quantity = 1, faces = 100)

View file

@ -2,8 +2,7 @@ package com.pixelized.desktop.lwa.business
import kotlin.math.truncate import kotlin.math.truncate
object SkillNormalizerUseCase { class SkillNormalizerUseCase {
fun normalize(value: Int): Int { fun normalize(value: Int): Int {
return (truncate(value.toFloat() / 5f) * 5f).toInt() return (truncate(value.toFloat() / 5f) * 5f).toInt()
} }

View file

@ -5,8 +5,7 @@ import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.round import kotlin.math.round
object SkillStepUseCase { class SkillStepUseCase {
val NONE: IntRange = -1..-1
data class SkillStep( data class SkillStep(
val criticalSuccess: IntRange, val criticalSuccess: IntRange,
@ -21,7 +20,8 @@ object SkillStepUseCase {
*/ */
fun computeSkillStep(skill: Int): SkillStep { fun computeSkillStep(skill: Int): SkillStep {
val criticalSuccess = 1..min(roundToInt { skill * 0.05f }, 99) val criticalSuccess = 1..min(roundToInt { skill * 0.05f }, 99)
val specialSuccess = (roundToInt { skill * 0.05f } + 1)..min(roundToInt { skill * 0.2f }, 99) val specialSuccess =
(roundToInt { skill * 0.05f } + 1)..min(roundToInt { skill * 0.2f }, 99)
val success = (roundToInt { skill * 0.2f } + 1)..min(skill, 99) val success = (roundToInt { skill * 0.2f } + 1)..min(skill, 99)
val criticalFailure = 100 - max(4 - criticalSuccess.last, 0)..100 val criticalFailure = 100 - max(4 - criticalSuccess.last, 0)..100
val failure = (success.last + 1) until criticalFailure.first val failure = (success.last + 1) until criticalFailure.first
@ -64,4 +64,8 @@ object SkillStepUseCase {
} }
println(")") println(")")
} }
companion object {
private val NONE: IntRange = -1..-1
}
} }

View file

@ -7,6 +7,7 @@ import kotlin.math.max
class SkillValueComputationUseCase( class SkillValueComputationUseCase(
private val arithmeticParser: ArithmeticParser, private val arithmeticParser: ArithmeticParser,
private val rollUseCase: RollUseCase,
) { ) {
fun computeSkillValue( fun computeSkillValue(
sheet: CharacterSheet, sheet: CharacterSheet,
@ -53,7 +54,7 @@ class SkillValueComputationUseCase(
print("Roll ->") print("Roll ->")
return arithmeticParser.parse(roll).sumOf { instruction -> return arithmeticParser.parse(roll).sumOf { instruction ->
val value = when (instruction) { val value = when (instruction) {
is Instruction.Dice -> RollUseCase.roll( is Instruction.Dice -> rollUseCase.roll(
quantity = instruction.quantity, quantity = instruction.quantity,
faces = instruction.faces faces = instruction.faces
) )
@ -67,7 +68,7 @@ class SkillValueComputationUseCase(
.parse(sheet.damageBonus) .parse(sheet.damageBonus)
.firstOrNull() .firstOrNull()
if (damageBonusInstructions is Instruction.Dice) { if (damageBonusInstructions is Instruction.Dice) {
RollUseCase.roll( rollUseCase.roll(
quantity = damageBonusInstructions.quantity, quantity = damageBonusInstructions.quantity,
faces = damageBonusInstructions.faces faces = damageBonusInstructions.faces
) )
@ -81,7 +82,7 @@ class SkillValueComputationUseCase(
.parse(sheet.damageBonus) .parse(sheet.damageBonus)
.firstOrNull() .firstOrNull()
if (damageBonusInstructions is Instruction.Dice) { if (damageBonusInstructions is Instruction.Dice) {
RollUseCase.roll( rollUseCase.roll(
quantity = damageBonusInstructions.quantity, quantity = damageBonusInstructions.quantity,
faces = damageBonusInstructions.faces / 2, faces = damageBonusInstructions.faces / 2,
) )

View file

@ -4,7 +4,6 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import com.pixelized.desktop.lwa.screen.main.MainPage import com.pixelized.desktop.lwa.screen.main.MainPage
import com.pixelized.desktop.lwa.screen.main.MainPageViewModel
object MainDestination { object MainDestination {
private const val ROUTE = "main" private const val ROUTE = "main"

View file

@ -1,4 +1,4 @@
package com.pixelized.desktop.lwa.repository.roll package com.pixelized.desktop.lwa.repository.roll_history
import com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.network.protocol.Message import com.pixelized.desktop.lwa.repository.network.protocol.Message

View file

@ -235,10 +235,11 @@ fun CharacterSheetPage(
value = it.value().text.toIntOrNull() ?: 0, value = it.value().text.toIntOrNull() ?: 0,
) )
viewModel.hideSubCharacteristicDialog() viewModel.hideSubCharacteristicDialog()
blurController.hide()
}, },
onDismissRequest = { onDismissRequest = {
blurController.hide()
viewModel.hideSubCharacteristicDialog() viewModel.hideSubCharacteristicDialog()
blurController.hide()
} }
) )
@ -249,10 +250,11 @@ fun CharacterSheetPage(
dialog = it dialog = it
) )
viewModel.hideDiminishedDialog() viewModel.hideDiminishedDialog()
blurController.hide()
}, },
onDismissRequest = { onDismissRequest = {
blurController.hide()
viewModel.hideDiminishedDialog() viewModel.hideDiminishedDialog()
blurController.hide()
}, },
) )
} }

View file

@ -1,14 +1,12 @@
package com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog package com.pixelized.desktop.lwa.screen.characterSheet.detail.dialog
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -29,13 +27,17 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.theme.LwaColorPalette import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__confirm_action
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__description import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__description
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__title import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__title
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
@Stable @Stable
@ -90,55 +92,64 @@ private fun Dialog(
indication = null, indication = null,
onClick = onDismissRequest, onClick = onDismissRequest,
) )
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.fillMaxSize() .fillMaxSize()
.padding(all = 32.dp), .padding(all = 32.dp),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Surface( DecoratedBox {
shape = remember { RoundedCornerShape(size = 16.dp) }, Surface {
) { Column(
Column( verticalArrangement = Arrangement.spacedBy(space = 24.dp),
verticalArrangement = Arrangement.spacedBy(space = 24.dp),
) {
Text(
modifier = Modifier
.padding(horizontal = 24.dp)
.padding(top = 24.dp),
style = MaterialTheme.typography.h6,
text = stringResource(Res.string.character_sheet__delete_dialog__title),
)
Text(
modifier = Modifier
.padding(horizontal = 24.dp),
style = MaterialTheme.typography.body1,
text = stringResource(
Res.string.character_sheet__delete_dialog__description,
dialog.name
),
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 4.dp),
horizontalArrangement = Arrangement.spacedBy(
space = 4.dp,
alignment = Alignment.End
)
) { ) {
TextButton( Text(
onClick = onDismissRequest, modifier = Modifier
) { .padding(horizontal = 24.dp)
Text( .padding(top = 24.dp),
text = stringResource(Res.string.character_sheet__delete_dialog__cancel_action) style = MaterialTheme.typography.h6,
text = stringResource(Res.string.character_sheet__delete_dialog__title),
)
Text(
modifier = Modifier
.padding(horizontal = 24.dp),
style = MaterialTheme.typography.body1,
text = stringResource(
Res.string.character_sheet__delete_dialog__description,
dialog.name
),
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 4.dp),
horizontalArrangement = Arrangement.spacedBy(
space = 4.dp,
alignment = Alignment.End
) )
}
TextButton(
onClick = { onConfirm(dialog) },
) { ) {
Text( TextButton(
text = stringResource(Res.string.character_sheet__delete_dialog__confirm_action) onClick = onDismissRequest,
) ) {
Text(
text = stringResource(Res.string.dialog__cancel_action)
)
}
TextButton(
onClick = { onConfirm(dialog) },
) {
Text(
text = stringResource(Res.string.dialog__confirm_action)
)
}
} }
} }
} }

View file

@ -22,6 +22,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
@ -32,12 +33,19 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
import org.jetbrains.compose.resources.stringResource
@Stable @Stable
data class StatChangeDialogUio( data class StatChangeDialogUio(
@ -102,6 +110,15 @@ private fun Dialog(
indication = null, indication = null,
onClick = onDismissRequest, onClick = onDismissRequest,
) )
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.fillMaxSize() .fillMaxSize()
.padding(all = 32.dp), .padding(all = 32.dp),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
@ -109,11 +126,11 @@ private fun Dialog(
DecoratedBox { DecoratedBox {
Surface { Surface {
Column( Column(
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp), verticalArrangement = Arrangement.spacedBy(space = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text( Text(
modifier = Modifier.padding(top = 16.dp, start = 24.dp, end = 24.dp),
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.caption,
text = dialog.label, text = dialog.label,
) )
@ -148,6 +165,32 @@ private fun Dialog(
text = dialog.maxValue, text = dialog.maxValue,
) )
} }
Row(
modifier = Modifier
.padding(bottom = 4.dp)
.padding(horizontal = 16.dp)
.align(alignment = Alignment.End),
horizontalArrangement = Arrangement.spacedBy(
space = 4.dp,
alignment = Alignment.End
)
) {
TextButton(
onClick = onDismissRequest,
) {
Text(
color = MaterialTheme.colors.primary.copy(alpha = .7f),
text = stringResource(Res.string.dialog__cancel_action)
)
}
TextButton(
onClick = { onConfirm(dialog) },
) {
Text(
text = stringResource(Res.string.dialog__confirm_action)
)
}
}
} }
} }
} }

View file

@ -12,6 +12,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@ -21,6 +22,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
@ -31,12 +33,19 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
import org.jetbrains.compose.resources.stringResource
@Stable @Stable
data class DiminishedStatDialogUio( data class DiminishedStatDialogUio(
@ -99,6 +108,16 @@ private fun Dialog(
indication = null, indication = null,
onClick = onDismissRequest, onClick = onDismissRequest,
) )
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.fillMaxSize() .fillMaxSize()
.padding(all = 32.dp), .padding(all = 32.dp),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
@ -106,11 +125,11 @@ private fun Dialog(
DecoratedBox { DecoratedBox {
Surface { Surface {
Column( Column(
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp), verticalArrangement = Arrangement.spacedBy(space = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text( Text(
modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 16.dp),
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.caption,
text = dialog.label, text = dialog.label,
) )
@ -132,6 +151,32 @@ private fun Dialog(
value = dialog.value(), value = dialog.value(),
onValueChange = dialog.onValueChange, onValueChange = dialog.onValueChange,
) )
Row(
modifier = Modifier
.padding(top = 4.dp)
.padding(horizontal = 16.dp)
.align(alignment = Alignment.End),
horizontalArrangement = Arrangement.spacedBy(
space = 4.dp,
alignment = Alignment.End
)
) {
TextButton(
onClick = onDismissRequest,
) {
Text(
color = MaterialTheme.colors.primary.copy(alpha = .7f),
text = stringResource(Res.string.dialog__cancel_action)
)
}
TextButton(
onClick = { onConfirm(dialog) },
) {
Text(
text = stringResource(Res.string.dialog__confirm_action)
)
}
}
} }
} }
} }

View file

@ -4,7 +4,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.pixelized.desktop.lwa.business.DamageBonusUseCase import com.pixelized.desktop.lwa.business.DamageBonusUseCase
import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase.normalize import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.SkillFieldFactory import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.occupation import com.pixelized.desktop.lwa.screen.characterSheet.edit.common.occupation
@ -21,7 +21,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__ch
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__int import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__int
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__pow import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__pow
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__str import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__characteristics__str
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__name_placeholder import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__name_label
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__acrobatics import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__acrobatics
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__aid import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__aid
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__athletics import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__athletics
@ -45,6 +45,8 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sk
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__armor import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__armor
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__damage_bonus import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__damage_bonus
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__max_hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__max_power_point
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__movement import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__movement
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point
import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.getString
@ -56,17 +58,12 @@ import kotlin.math.min
class CharacterSheetEditFactory( class CharacterSheetEditFactory(
private val bonusDamageUseCase: DamageBonusUseCase, private val bonusDamageUseCase: DamageBonusUseCase,
private val skillFactory: SkillFieldFactory, private val skillFactory: SkillFieldFactory,
private val normalizer: SkillNormalizerUseCase,
) { ) {
fun updateCharacterSheet( fun updateCharacterSheet(
currentSheet: CharacterSheet?, currentSheet: CharacterSheet?,
editedSheet: CharacterSheetEditPageUio, editedSheet: CharacterSheetEditPageUio,
): CharacterSheet { ): CharacterSheet {
val maxHp = editedSheet.maxHp.unpack()?.toIntOrNull()
?: currentSheet?.maxHp
?: 0
val maxPp = editedSheet.maxPp.unpack()?.toIntOrNull()
?: currentSheet?.maxPp
?: 0
return CharacterSheet( return CharacterSheet(
id = editedSheet.id, id = editedSheet.id,
name = editedSheet.name.value.value, name = editedSheet.name.value.value,
@ -96,11 +93,19 @@ class CharacterSheetEditFactory(
?: currentSheet?.movement ?: currentSheet?.movement
?: 10, ?: 10,
overrideMaxHp = editedSheet.maxHp.value.value.value.isNotBlank(), overrideMaxHp = editedSheet.maxHp.value.value.value.isNotBlank(),
maxHp = maxHp, maxHp = editedSheet.maxHp.unpack()?.toIntOrNull()
currentHp = max(0, min(maxHp, currentSheet?.currentHp ?: maxHp)), ?: currentSheet?.maxHp
?: 0,
currentHp = editedSheet.currentHp.unpack()?.toIntOrNull()
?: currentSheet?.currentHp
?: 0,
overrideMaxPP = editedSheet.maxPp.value.value.value.isNotBlank(), overrideMaxPP = editedSheet.maxPp.value.value.value.isNotBlank(),
maxPp = maxPp, maxPp = editedSheet.maxPp.unpack()?.toIntOrNull()
currentPp = max(0, min(maxPp, currentSheet?.currentPp ?: maxPp)), ?: currentSheet?.maxPp
?: 0,
currentPp = editedSheet.currentPp.unpack()?.toIntOrNull()
?: currentSheet?.currentPp
?: 0,
overrideDamageBonus = editedSheet.damageBonus.value.value.value.isNotBlank(), overrideDamageBonus = editedSheet.damageBonus.value.value.value.isNotBlank(),
damageBonus = editedSheet.damageBonus.unpack() damageBonus = editedSheet.damageBonus.unpack()
?: currentSheet?.damageBonus ?: currentSheet?.damageBonus
@ -226,10 +231,25 @@ class CharacterSheetEditFactory(
val specialSkillsLabel = getString(Res.string.character_sheet_edit__skills__special_title) val specialSkillsLabel = getString(Res.string.character_sheet_edit__skills__special_title)
val magicSkillsLabel = getString(Res.string.character_sheet_edit__skills__magic_title) val magicSkillsLabel = getString(Res.string.character_sheet_edit__skills__magic_title)
val maxHitPoint = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__max_hit_point),
value = skillFactory.createWrapper(
value = if (sheet?.overrideMaxHp == true) "${sheet.maxHp}" else "",
placeholder = derivedStateOf { "${ceil((con() + hei()) / 2f).toInt()}" },
)
)
val maxPowerPoint = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__max_power_point),
value = skillFactory.createWrapper(
value = if (sheet?.overrideMaxPP == true) "${sheet.maxPp}" else "",
placeholder = derivedStateOf { "${pow()}" },
)
)
return CharacterSheetEditPageUio( return CharacterSheetEditPageUio(
id = sheet?.id ?: UUID.randomUUID().toString(), id = sheet?.id ?: UUID.randomUUID().toString(),
name = skillFactory.createWrapper( name = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__name_placeholder), label = mutableStateOf(getString(Res.string.character_sheet_edit__name_label)),
value = sheet?.name ?: "" value = sheet?.name ?: ""
), ),
strength = str, strength = str,
@ -246,18 +266,32 @@ class CharacterSheetEditFactory(
placeholder = mutableStateOf("10"), placeholder = mutableStateOf("10"),
) )
), ),
maxHp = SimpleFieldUio( maxHp = maxHitPoint,
currentHp = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__hit_point), label = getString(Res.string.character_sheet_edit__sub_characteristics__hit_point),
value = skillFactory.createWrapper( value = skillFactory.createWrapper(
value = if (sheet?.overrideMaxHp == true) "${sheet.maxHp}" else "", enable = false,
placeholder = derivedStateOf { "${ceil((con() + hei()) / 2f).toInt()}" }, placeholder = derivedStateOf {
val min = min(
sheet?.currentHp ?: Int.MAX_VALUE,
maxHitPoint.unpack()?.toIntOrNull() ?: Int.MAX_VALUE,
)
if (min != Int.MAX_VALUE) "$min" else ""
},
) )
), ),
maxPp = SimpleFieldUio( maxPp = maxPowerPoint,
currentPp = SimpleFieldUio(
label = getString(Res.string.character_sheet_edit__sub_characteristics__power_point), label = getString(Res.string.character_sheet_edit__sub_characteristics__power_point),
value = skillFactory.createWrapper( value = skillFactory.createWrapper(
value = if (sheet?.overrideMaxPP == true) "${sheet.maxPp}" else "", enable = false,
placeholder = derivedStateOf { "${pow()}" }, placeholder = derivedStateOf {
val min = min(
sheet?.currentPp ?: Int.MAX_VALUE,
maxPowerPoint.unpack()?.toIntOrNull() ?: Int.MAX_VALUE,
)
if (min != Int.MAX_VALUE) "$min" else ""
},
) )
), ),
damageBonus = SimpleFieldUio( damageBonus = SimpleFieldUio(
@ -284,97 +318,97 @@ class CharacterSheetEditFactory(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.COMBAT_ID, id = CharacterSheet.CommonSkillId.COMBAT_ID,
label = getString(Res.string.character_sheet_edit__skills__combat), label = getString(Res.string.character_sheet_edit__skills__combat),
base = derivedStateOf { normalize(dex() * 2) }, base = derivedStateOf { normalizer.normalize(dex() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.DODGE_ID, id = CharacterSheet.CommonSkillId.DODGE_ID,
label = getString(Res.string.character_sheet_edit__skills__dodge), label = getString(Res.string.character_sheet_edit__skills__dodge),
base = derivedStateOf { normalize(dex() * 2) }, base = derivedStateOf { normalizer.normalize(dex() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.GRAB_ID, id = CharacterSheet.CommonSkillId.GRAB_ID,
label = getString(Res.string.character_sheet_edit__skills__grab), label = getString(Res.string.character_sheet_edit__skills__grab),
base = derivedStateOf { normalize(str() + hei()) }, base = derivedStateOf { normalizer.normalize(str() + hei()) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.THROW_ID, id = CharacterSheet.CommonSkillId.THROW_ID,
label = getString(Res.string.character_sheet_edit__skills__throw), label = getString(Res.string.character_sheet_edit__skills__throw),
base = derivedStateOf { normalize(str() + dex()) }, base = derivedStateOf { normalizer.normalize(str() + dex()) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.ATHLETICS_ID, id = CharacterSheet.CommonSkillId.ATHLETICS_ID,
label = getString(Res.string.character_sheet_edit__skills__athletics), label = getString(Res.string.character_sheet_edit__skills__athletics),
base = derivedStateOf { normalize(str() + con() * 2) }, base = derivedStateOf { normalizer.normalize(str() + con() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.ACROBATICS_ID, id = CharacterSheet.CommonSkillId.ACROBATICS_ID,
label = getString(Res.string.character_sheet_edit__skills__acrobatics), label = getString(Res.string.character_sheet_edit__skills__acrobatics),
base = derivedStateOf { normalize(dex() + con() * 2) }, base = derivedStateOf { normalizer.normalize(dex() + con() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.PERCEPTION_ID, id = CharacterSheet.CommonSkillId.PERCEPTION_ID,
label = getString(Res.string.character_sheet_edit__skills__perception), label = getString(Res.string.character_sheet_edit__skills__perception),
base = derivedStateOf { normalize(10 + int() * 2) }, base = derivedStateOf { normalizer.normalize(10 + int() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.SEARCH_ID, id = CharacterSheet.CommonSkillId.SEARCH_ID,
label = getString(Res.string.character_sheet_edit__skills__search), label = getString(Res.string.character_sheet_edit__skills__search),
base = derivedStateOf { normalize(10 + int() * 2) }, base = derivedStateOf { normalizer.normalize(10 + int() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.EMPATHY_ID, id = CharacterSheet.CommonSkillId.EMPATHY_ID,
label = getString(Res.string.character_sheet_edit__skills__empathy), label = getString(Res.string.character_sheet_edit__skills__empathy),
base = derivedStateOf { normalize(cha() + int()) }, base = derivedStateOf { normalizer.normalize(cha() + int()) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.PERSUASION_ID, id = CharacterSheet.CommonSkillId.PERSUASION_ID,
label = getString(Res.string.character_sheet_edit__skills__persuasion), label = getString(Res.string.character_sheet_edit__skills__persuasion),
base = derivedStateOf { normalize(cha() * 3) }, base = derivedStateOf { normalizer.normalize(cha() * 3) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.INTIMIDATION_ID, id = CharacterSheet.CommonSkillId.INTIMIDATION_ID,
label = getString(Res.string.character_sheet_edit__skills__intimidation), label = getString(Res.string.character_sheet_edit__skills__intimidation),
base = derivedStateOf { normalize(cha() + max(pow(), hei()) * 2) }, base = derivedStateOf { normalizer.normalize(cha() + max(pow(), hei()) * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.SPIEL_ID, id = CharacterSheet.CommonSkillId.SPIEL_ID,
label = getString(Res.string.character_sheet_edit__skills__spiel), label = getString(Res.string.character_sheet_edit__skills__spiel),
base = derivedStateOf { normalize(cha() * 2 + int()) }, base = derivedStateOf { normalizer.normalize(cha() * 2 + int()) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.BARGAIN_ID, id = CharacterSheet.CommonSkillId.BARGAIN_ID,
label = getString(Res.string.character_sheet_edit__skills__bargain), label = getString(Res.string.character_sheet_edit__skills__bargain),
base = derivedStateOf { normalize(cha() * 2) }, base = derivedStateOf { normalizer.normalize(cha() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.DISCRETION_ID, id = CharacterSheet.CommonSkillId.DISCRETION_ID,
label = getString(Res.string.character_sheet_edit__skills__discretion), label = getString(Res.string.character_sheet_edit__skills__discretion),
base = derivedStateOf { normalize(cha() + dex() * 2 - hei()) }, base = derivedStateOf { normalizer.normalize(cha() + dex() * 2 - hei()) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID, id = CharacterSheet.CommonSkillId.SLEIGHT_OF_HAND_ID,
label = getString(Res.string.character_sheet_edit__skills__sleight_of_hand), label = getString(Res.string.character_sheet_edit__skills__sleight_of_hand),
base = derivedStateOf { normalize(dex() * 2) }, base = derivedStateOf { normalizer.normalize(dex() * 2) },
), ),
createBaseSkill( createBaseSkill(
sheet = sheet, sheet = sheet,
id = CharacterSheet.CommonSkillId.AID_ID, id = CharacterSheet.CommonSkillId.AID_ID,
label = getString(Res.string.character_sheet_edit__skills__aid), label = getString(Res.string.character_sheet_edit__skills__aid),
base = derivedStateOf { normalize(int() + dex()) }, base = derivedStateOf { normalizer.normalize(int() + dex()) },
), ),
), ),
specialSkills = sheet?.specialSkills?.map { skill -> specialSkills = sheet?.specialSkills?.map { skill ->
@ -415,11 +449,11 @@ class CharacterSheetEditFactory(
ActionFieldUio( ActionFieldUio(
id = action.id, id = action.id,
label = skillFactory.createWrapper( label = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__actions__name_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__name_label)),
value = action.label, value = action.label,
), ),
action = skillFactory.createWrapper( action = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__actions__action_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__action_label)),
value = action.roll, value = action.roll,
), ),
option = skillFactory.deleteOption { onDeleteSkill(action.id) }, option = skillFactory.deleteOption { onDeleteSkill(action.id) },
@ -441,11 +475,11 @@ class CharacterSheetEditFactory(
label = label, label = label,
base = base, base = base,
bonus = skillFactory.createWrapper( bonus = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__skills__bonus_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__bonus_label)),
value = skill?.bonus?.toString() ?: "", value = skill?.bonus?.toString() ?: "",
), ),
level = skillFactory.createWrapper( level = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__skills__level_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__level_label)),
value = skill?.level?.toString() ?: "", value = skill?.level?.toString() ?: "",
), ),
option = skillFactory.occupationOption(skill?.occupation ?: false), option = skillFactory.occupationOption(skill?.occupation ?: false),

View file

@ -69,7 +69,9 @@ data class CharacterSheetEditPageUio(
val charisma: SimpleFieldUio, val charisma: SimpleFieldUio,
val movement: SimpleFieldUio, val movement: SimpleFieldUio,
val maxHp: SimpleFieldUio, val maxHp: SimpleFieldUio,
val currentHp: SimpleFieldUio,
val maxPp: SimpleFieldUio, val maxPp: SimpleFieldUio,
val currentPp: SimpleFieldUio,
val damageBonus: SimpleFieldUio, val damageBonus: SimpleFieldUio,
val armor: SimpleFieldUio, val armor: SimpleFieldUio,
val commonSkills: List<BaseSkillFieldUio>, val commonSkills: List<BaseSkillFieldUio>,
@ -92,7 +94,9 @@ data class CharacterSheetEditPageUio(
get() = listOf( get() = listOf(
movement, movement,
maxHp, maxHp,
currentHp,
maxPp, maxPp,
currentPp,
damageBonus, damageBonus,
armor, armor,
) )
@ -187,54 +191,60 @@ fun CharacterSheetEdit(
wrapper = form.name, wrapper = form.name,
) )
DecoratedBox( Row(
modifier = Modifier.animateContentSize(), horizontalArrangement = Arrangement.spacedBy(space = 16.dp)
) { ) {
Column( DecoratedBox(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.weight(weight = 1f),
) { ) {
Text( Column(
modifier = Modifier.padding(vertical = 8.dp), horizontalAlignment = Alignment.CenterHorizontally,
overflow = TextOverflow.Ellipsis, ) {
maxLines = 1, Text(
style = MaterialTheme.typography.caption, modifier = Modifier.padding(vertical = 8.dp),
text = stringResource(Res.string.character_sheet_edit__characteristics__title), overflow = TextOverflow.Ellipsis,
) maxLines = 1,
form.characteristics.forEach { style = MaterialTheme.typography.caption,
SimpleField( text = stringResource(Res.string.character_sheet_edit__characteristics__title),
modifier = Modifier.fillMaxWidth(),
field = it,
) )
form.characteristics.forEach {
SimpleField(
modifier = Modifier.fillMaxWidth(),
field = it,
)
}
}
}
DecoratedBox(
modifier = Modifier.weight(weight = 1f),
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
modifier = Modifier.padding(vertical = 8.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.caption,
text = stringResource(Res.string.character_sheet_edit__sub_characteristics__title),
)
form.subCharacteristics.forEach {
SimpleField(
modifier = Modifier.fillMaxWidth(),
field = it,
)
}
} }
} }
} }
DecoratedBox( DecoratedBox(
modifier = Modifier.animateContentSize(), modifier = Modifier
) { .fillMaxWidth()
Column( .animateContentSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
modifier = Modifier.padding(vertical = 8.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.caption,
text = stringResource(Res.string.character_sheet_edit__sub_characteristics__title),
)
form.subCharacteristics.forEach {
SimpleField(
modifier = Modifier.fillMaxWidth(),
field = it,
)
}
}
}
DecoratedBox(
modifier = Modifier.animateContentSize(),
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text( Text(
@ -256,9 +266,12 @@ fun CharacterSheetEdit(
} }
DecoratedBox( DecoratedBox(
modifier = Modifier.animateContentSize(), modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text( Text(
@ -299,9 +312,12 @@ fun CharacterSheetEdit(
} }
DecoratedBox( DecoratedBox(
modifier = Modifier.animateContentSize(), modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text( Text(

View file

@ -79,10 +79,10 @@ class CharacterSheetEditViewModel(
val field = ActionFieldUio( val field = ActionFieldUio(
id = id, id = id,
label = skillFactory.createWrapper( label = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__actions__name_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__name_label)),
), ),
action = skillFactory.createWrapper( action = skillFactory.createWrapper(
label = getString(Res.string.character_sheet_edit__actions__action_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__actions__action_label)),
), ),
option = skillFactory.deleteOption { deleteSkill(id) }, option = skillFactory.deleteOption { deleteSkill(id) },
) )

View file

@ -3,6 +3,7 @@ package com.pixelized.desktop.lwa.screen.characterSheet.edit.common
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.SkillFieldUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.SkillFieldUio
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.ActionOption import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.ActionOption
@ -29,22 +30,28 @@ class SkillFieldFactory {
levelValue: String = "", levelValue: String = "",
options: List<OptionUio> = emptyList(), options: List<OptionUio> = emptyList(),
): SkillFieldUio { ): SkillFieldUio {
val baseLabel = getString(Res.string.character_sheet_edit__skills__base_label)
return SkillFieldUio( return SkillFieldUio(
id = id, id = id,
label = createWrapper( label = createWrapper(
label = label, label = mutableStateOf(labelValue),
value = labelValue, value = labelValue,
), ),
base = createWrapper( base = createWrapper(
label = getString(Res.string.character_sheet_edit__skills__base_label), label = derivedStateOf {
options
.firstOrNull { it is CheckedOption && it.checked.value }
?.let { "$baseLabel *" }
?: baseLabel
},
value = baseValue, value = baseValue,
), ),
bonus = createWrapper( bonus = createWrapper(
label = getString(Res.string.character_sheet_edit__skills__bonus_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__bonus_label)),
value = bonusValue, value = bonusValue,
), ),
level = createWrapper( level = createWrapper(
label = getString(Res.string.character_sheet_edit__skills__level_label), label = mutableStateOf(getString(Res.string.character_sheet_edit__skills__level_label)),
value = levelValue, value = levelValue,
), ),
options = options, options = options,
@ -53,7 +60,7 @@ class SkillFieldFactory {
fun createWrapper( fun createWrapper(
enable: Boolean = true, enable: Boolean = true,
label: String? = null, label: State<String?> = mutableStateOf(null),
placeholder: State<String?> = mutableStateOf(null), placeholder: State<String?> = mutableStateOf(null),
value: String = "", value: String = "",
): TextFieldWrapperUio { ): TextFieldWrapperUio {

View file

@ -17,6 +17,7 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -47,6 +48,17 @@ fun BaseSkillForm(
field: BaseSkillFieldUio, field: BaseSkillFieldUio,
) { ) {
val showMenu = remember { mutableStateOf(false) } val showMenu = remember { mutableStateOf(false) }
val baseLabel = stringResource(Res.string.character_sheet_edit__skills__base_label)
.let { label ->
remember {
derivedStateOf {
when (field.option.checked.value) {
true -> "$label *"
else -> label
}
}
}
}
Row( Row(
modifier = modifier, modifier = modifier,
@ -73,7 +85,7 @@ fun BaseSkillForm(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
maxLines = 1, maxLines = 1,
color = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium), color = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),
text = stringResource(Res.string.character_sheet_edit__skills__base_label), text = baseLabel.value,
) )
Text( Text(
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.body1,

View file

@ -1,10 +1,7 @@
package com.pixelized.desktop.lwa.screen.characterSheet.edit.composable package com.pixelized.desktop.lwa.screen.characterSheet.edit.composable
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box 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.Row
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material.DropdownMenu import androidx.compose.material.DropdownMenu
@ -12,7 +9,6 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
@ -21,15 +17,10 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.singleWindowApplication
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield.TextFieldWrapperUio
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield.TextFieldWrapper
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.ActionOption
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.CheckedOption
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.DropDownMenuItemWrapper import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.DropDownMenuItemWrapper
import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.OptionUio import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.option.OptionUio
import com.pixelized.desktop.lwa.utils.preview.ContentPreview import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield.TextFieldWrapper
import java.util.UUID import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield.TextFieldWrapperUio
@Stable @Stable
class SkillFieldUio( class SkillFieldUio(

View file

@ -1,5 +1,6 @@
package com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield package com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.textfield
import androidx.compose.foundation.layout.height
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextField import androidx.compose.material.TextField
@ -7,17 +8,18 @@ import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.utils.rememberKeyboardActions import com.pixelized.desktop.lwa.utils.rememberKeyboardActions
@Stable @Stable
data class TextFieldWrapperUio( data class TextFieldWrapperUio(
val enable: Boolean, val enable: Boolean,
val label: String?, val label: State<String?>,
val value: State<String>, val value: State<String>,
val placeholder: State<String?>, val placeholder: State<String?>,
val onValueChange: (String) -> Unit, val onValueChange: (String) -> Unit,
@ -28,12 +30,18 @@ fun TextFieldWrapper(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
wrapper: TextFieldWrapperUio, wrapper: TextFieldWrapperUio,
) { ) {
val colorScheme = MaterialTheme.colors
val focus = LocalFocusManager.current val focus = LocalFocusManager.current
TextField( TextField(
modifier = modifier, modifier = Modifier.height(height = 56.dp).then(other = modifier),
colors = TextFieldDefaults.textFieldColors( colors = TextFieldDefaults.textFieldColors(
backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.03f), backgroundColor = remember(wrapper.enable) {
when (wrapper.enable) {
true -> colorScheme.onSurface.copy(alpha = 0.03f)
else -> colorScheme.surface
}
},
), ),
keyboardActions = rememberKeyboardActions { keyboardActions = rememberKeyboardActions {
focus.moveFocus(FocusDirection.Next) focus.moveFocus(FocusDirection.Next)
@ -49,7 +57,7 @@ fun TextFieldWrapper(
) )
} }
}, },
label = wrapper.label?.let { label = wrapper.label.value?.let {
{ {
Text( Text(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,

View file

@ -10,7 +10,7 @@ import com.pixelized.desktop.lwa.business.SkillStepUseCase
import com.pixelized.desktop.lwa.business.SkillValueComputationUseCase import com.pixelized.desktop.lwa.business.SkillValueComputationUseCase
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
import com.pixelized.desktop.lwa.repository.roll.RollHistoryRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio
import com.pixelized.desktop.lwa.screen.roll.DifficultyUio.Difficulty import com.pixelized.desktop.lwa.screen.roll.DifficultyUio.Difficulty
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -34,6 +34,7 @@ class RollViewModel(
private val characterSheetRepository: CharacterSheetRepository, private val characterSheetRepository: CharacterSheetRepository,
private val rollHistoryRepository: RollHistoryRepository, private val rollHistoryRepository: RollHistoryRepository,
private val skillComputation: SkillValueComputationUseCase, private val skillComputation: SkillValueComputationUseCase,
private val skillStepUseCase: SkillStepUseCase,
) : ViewModel() { ) : ViewModel() {
private lateinit var sheet: CharacterSheet private lateinit var sheet: CharacterSheet
@ -107,7 +108,7 @@ class RollViewModel(
this.rollSuccessValue = rollSuccessValue this.rollSuccessValue = rollSuccessValue
val rollStep = rollSuccessValue?.let { val rollStep = rollSuccessValue?.let {
SkillStepUseCase.computeSkillStep(skill = it) skillStepUseCase.computeSkillStep(skill = it)
} }
_rollResult.value = null _rollResult.value = null
@ -158,7 +159,7 @@ class RollViewModel(
delay(500) delay(500)
val rollStep = rollSuccessValue?.let { val rollStep = rollSuccessValue?.let {
SkillStepUseCase.computeSkillStep( skillStepUseCase.computeSkillStep(
skill = when (_rollDifficulty.value?.difficulty) { skill = when (_rollDifficulty.value?.difficulty) {
Difficulty.EASY -> it * 2 Difficulty.EASY -> it * 2
Difficulty.NORMAL -> it Difficulty.NORMAL -> it
@ -221,7 +222,7 @@ class RollViewModel(
difficulty = difficulty, difficulty = difficulty,
) )
val rollStep = rollSuccessValue?.let { val rollStep = rollSuccessValue?.let {
SkillStepUseCase.computeSkillStep( skillStepUseCase.computeSkillStep(
skill = when (_rollDifficulty.value?.difficulty) { skill = when (_rollDifficulty.value?.difficulty) {
Difficulty.EASY -> it * 2 Difficulty.EASY -> it * 2
Difficulty.NORMAL -> it Difficulty.NORMAL -> it

View file

@ -20,7 +20,6 @@ import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.desktop.lwa.navigation.screen.LocalScreenController import com.pixelized.desktop.lwa.navigation.screen.LocalScreenController
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.roll_history__title import lwacharactersheet.composeapp.generated.resources.roll_history__title

View file

@ -5,7 +5,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.network.protocol.RollMessage import com.pixelized.desktop.lwa.repository.network.protocol.RollMessage
import com.pixelized.desktop.lwa.repository.roll.RollHistoryRepository import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class RollHistoryViewModel( class RollHistoryViewModel(

View file

@ -1,27 +0,0 @@
package com.pixelized.desktop.lwa.utils.extention
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
@Suppress("UNCHECKED_CAST")
fun <T> ByteArray.fromByteArray(): T {
val byteArrayInputStream = ByteArrayInputStream(this)
val objectInput = ObjectInputStream(byteArrayInputStream)
val result = objectInput.readObject() as T
objectInput.close()
byteArrayInputStream.close()
return result
}
fun Any.toByteArray(): ByteArray {
val byteArrayOutputStream = ByteArrayOutputStream()
val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
objectOutputStream.writeObject(this)
objectOutputStream.flush()
val result = byteArrayOutputStream.toByteArray()
byteArrayOutputStream.close()
objectOutputStream.close()
return result
}

View file

@ -7,7 +7,6 @@ androidx-lifecycle = "2.8.3"
androidx-navigation = "2.8.0-alpha10" androidx-navigation = "2.8.0-alpha10"
ktor_version = "3.0.0" ktor_version = "3.0.0"
koin = "4.0.0" koin = "4.0.0"
koinComposeMultiplatform = "1.2.0-Beta4"
[plugins] [plugins]
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" } composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
@ -15,7 +14,6 @@ composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "k
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
[libraries] [libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }