Basic character sheet edit.
This commit is contained in:
		
							parent
							
								
									6e4f91e007
								
							
						
					
					
						commit
						d74a5fcd7c
					
				
					 3 changed files with 501 additions and 2 deletions
				
			
		|  | @ -1,20 +1,31 @@ | |||
| package com.pixelized.desktop.lwa | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.Icon | ||||
| import androidx.compose.material.IconButton | ||||
| import androidx.compose.material.Surface | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Edit | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.window.Window | ||||
| import androidx.compose.ui.window.rememberWindowState | ||||
| import androidx.lifecycle.viewmodel.compose.viewModel | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheet | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetUio | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetViewModel | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEdit | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditUio | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.edit.FieldUio | ||||
| import com.pixelized.desktop.lwa.screen.overlay.BlurOverlay | ||||
| import com.pixelized.desktop.lwa.screen.overlay.BlurOverlayViewModel | ||||
| import com.pixelized.desktop.lwa.screen.roll.RollPage | ||||
|  | @ -33,13 +44,34 @@ fun App() { | |||
|             val overlayViewModel = viewModel { BlurOverlayViewModel() } | ||||
|             val rollViewModel = viewModel { RollViewModel() } | ||||
| 
 | ||||
|             val edit = remember { mutableStateOf<CharacterSheetEditUio?>(null) } | ||||
| 
 | ||||
|             Column( | ||||
|                 modifier = Modifier.padding(all = 16.dp), | ||||
|             ) { | ||||
|                 Row { | ||||
|                     Button( | ||||
|                         onClick = sheetViewModel::showCharacterSheet, | ||||
|                     ) { | ||||
|                         Text(text = "Koryas Tissenpa") | ||||
|                     } | ||||
|                     IconButton( | ||||
|                         onClick = { | ||||
|                             edit.value = CharacterSheetEditUio.create( | ||||
|                                 sheet = CharacterSheetUio.Koryas, | ||||
|                             ) | ||||
|                         } | ||||
|                     ) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.Edit, | ||||
|                             contentDescription = null | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|                 Button( | ||||
|                     onClick = sheetViewModel::showCharacterSheet, | ||||
|                     onClick = { edit.value = CharacterSheetEditUio.Default }, | ||||
|                 ) { | ||||
|                     Text(text = "Koryas Tissenpa") | ||||
|                     Text(text = "Créer une feuille de personnage") | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|  | @ -82,6 +114,46 @@ fun App() { | |||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             edit.value?.let { sheet -> | ||||
|                 Window( | ||||
|                     onCloseRequest = { edit.value = null }, | ||||
|                     state = rememberWindowState( | ||||
|                         width = 320.dp + 64.dp, | ||||
|                         height = 900.dp, | ||||
|                     ), | ||||
|                     title = "LwaCharacterSheet", | ||||
|                 ) { | ||||
|                     Surface( | ||||
|                         modifier = Modifier.fillMaxSize(), | ||||
|                     ) { | ||||
|                         CharacterSheetEdit( | ||||
|                             form = sheet, | ||||
|                             onSkill = { skill -> | ||||
|                                 edit.value = sheet.copy( | ||||
|                                     groups = sheet.groups.map { group -> | ||||
|                                         if (skill.title == group.title) { | ||||
|                                             group.copy( | ||||
|                                                 fields = mutableListOf<FieldUio>().apply { | ||||
|                                                     addAll(group.fields) | ||||
|                                                     add( | ||||
|                                                         FieldUio.create( | ||||
|                                                             label = "", | ||||
|                                                             valuePlaceHolder = { "40" }, | ||||
|                                                         ) | ||||
|                                                     ) | ||||
|                                                 } | ||||
|                                             ) | ||||
|                                         } else { | ||||
|                                             group | ||||
|                                         } | ||||
|                                     } | ||||
|                                 ) | ||||
|                             } | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,323 @@ | |||
| package com.pixelized.desktop.lwa.screen.characterSheet.edit | ||||
| 
 | ||||
| import androidx.compose.animation.animateContentSize | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.ButtonDefaults | ||||
| import androidx.compose.material.Icon | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Add | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.Stable | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import com.pixelized.desktop.lwa.composable.DecoratedBox | ||||
| import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetUio | ||||
| import kotlin.math.max | ||||
| import kotlin.math.truncate | ||||
| 
 | ||||
| @Stable | ||||
| data class CharacterSheetEditUio( | ||||
|     val name: FieldUio, | ||||
|     val groups: List<Group>, | ||||
| ) { | ||||
|     @Stable | ||||
|     data class Group( | ||||
|         val title: String, | ||||
|         val editable: Boolean = false, | ||||
|         val fields: List<FieldUio>, | ||||
|     ) | ||||
| 
 | ||||
|     companion object { | ||||
|         val Default = run { | ||||
|             val strField = FieldUio.create(label = "Force", valuePlaceHolder = { "0" }) | ||||
|             fun str(): Int = strField.value.value.toIntOrNull() ?: 0 | ||||
|             val dexField = FieldUio.create(label = "Dextérité", valuePlaceHolder = { "0" }) | ||||
|             fun dex(): Int = dexField.value.value.toIntOrNull() ?: 0 | ||||
|             val conField = FieldUio.create(label = "Constitution", valuePlaceHolder = { "0" }) | ||||
|             fun con(): Int = conField.value.value.toIntOrNull() ?: 0 | ||||
|             val vitField = FieldUio.create(label = "Taille", valuePlaceHolder = { "0" }) | ||||
|             fun vit(): Int = vitField.value.value.toIntOrNull() ?: 0 | ||||
|             val intField = FieldUio.create(label = "Intelligence", valuePlaceHolder = { "0" }) | ||||
|             fun int(): Int = intField.value.value.toIntOrNull() ?: 0 | ||||
|             val powField = FieldUio.create(label = "Pouvoir", valuePlaceHolder = { "0" }) | ||||
|             fun pow(): Int = powField.value.value.toIntOrNull() ?: 0 | ||||
|             val chaField = FieldUio.create(label = "Charisme", valuePlaceHolder = { "0" }) | ||||
|             fun cha(): Int = chaField.value.value.toIntOrNull() ?: 0 | ||||
| 
 | ||||
|             CharacterSheetEditUio( | ||||
|                 name = FieldUio.create( | ||||
|                     useLabelAsPlaceholder = true, | ||||
|                     label = "Name", | ||||
|                 ), | ||||
|                 groups = listOf( | ||||
|                     Group( | ||||
|                         title = "Charactéristiques", | ||||
|                         fields = listOf( | ||||
|                             strField, | ||||
|                             dexField, | ||||
|                             conField, | ||||
|                             vitField, | ||||
|                             intField, | ||||
|                             powField, | ||||
|                             chaField | ||||
|                         ), | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Charactéristiques dérivées", | ||||
|                         fields = listOf( | ||||
|                             FieldUio.create( | ||||
|                                 label = "Déplacement", | ||||
|                                 valuePlaceHolder = { "10" }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Points de vie", | ||||
|                                 valuePlaceHolder = { "${(con() + vit()) / 2}" }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Points de pouvoir", | ||||
|                                 valuePlaceHolder = { "${pow()}" }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Bonus aux dégats", | ||||
|                                 valuePlaceHolder = { | ||||
|                                     val bonus = str() + vit() | ||||
|                                     when { | ||||
|                                         bonus < 12 -> "-1d6" | ||||
|                                         bonus in 12..17 -> "-1d4" | ||||
|                                         bonus in 18..22 -> "-0" | ||||
|                                         bonus in 23..29 -> "1d4" | ||||
|                                         bonus in 30..39 -> "1d6" | ||||
|                                         else -> "2d6" | ||||
|                                     } | ||||
|                                 }, | ||||
|                             ), | ||||
|                             FieldUio.create(label = "Armure", valuePlaceHolder = { "0" }), | ||||
|                         ), | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Compétances", | ||||
|                         editable = true, | ||||
|                         fields = listOf( | ||||
|                             FieldUio.create( | ||||
|                                 label = "Bagarre", | ||||
|                                 valuePlaceHolder = { trunc(dex() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Esquive", | ||||
|                                 valuePlaceHolder = { trunc(dex() * 2) } | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Saisie", | ||||
|                                 valuePlaceHolder = { trunc(str() + vit()) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Lancer", | ||||
|                                 valuePlaceHolder = { trunc(str() + dex()) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Athlétisme", | ||||
|                                 valuePlaceHolder = { trunc(str() + con() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Acrobatie", | ||||
|                                 valuePlaceHolder = { trunc(dex() + con() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Perception", | ||||
|                                 valuePlaceHolder = { trunc(10 + int() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Recherche", | ||||
|                                 valuePlaceHolder = { trunc(10 + int() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Empathie", | ||||
|                                 valuePlaceHolder = { trunc(cha() + int()) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Persuasion", | ||||
|                                 valuePlaceHolder = { trunc(cha() * 3) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Intimidation", | ||||
|                                 valuePlaceHolder = { trunc(cha() + max(pow(), vit()) * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Baratin", | ||||
|                                 valuePlaceHolder = { trunc(cha() * 2 + int()) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Marchandage", | ||||
|                                 valuePlaceHolder = { trunc(cha() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Discrétion", | ||||
|                                 valuePlaceHolder = { trunc(cha() + dex() * 2 - vit()) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Escamotage", | ||||
|                                 valuePlaceHolder = { trunc(dex() * 2) }, | ||||
|                             ), | ||||
|                             FieldUio.create( | ||||
|                                 label = "Premiers soins", | ||||
|                                 valuePlaceHolder = { trunc(int() + dex()) }, | ||||
|                             ), | ||||
|                         ), | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Occupations", | ||||
|                         editable = true, | ||||
|                         fields = emptyList(), | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Compétences magiques", | ||||
|                         editable = true, | ||||
|                         fields = emptyList(), | ||||
|                     ), | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         fun create(sheet: CharacterSheetUio): CharacterSheetEditUio { | ||||
|             return CharacterSheetEditUio( | ||||
|                 name = FieldUio.create( | ||||
|                     useLabelAsPlaceholder = true, | ||||
|                     label = "Name", | ||||
|                     initialValue = sheet.name, | ||||
|                 ), | ||||
|                 groups = listOf( | ||||
|                     Group( | ||||
|                         title = "Charactéristiques", | ||||
|                         fields = sheet.characteristics.map { | ||||
|                             FieldUio.create( | ||||
|                                 label = it.label, | ||||
|                                 initialValue = it.value, | ||||
|                             ) | ||||
|                         }, | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Charactéristiques dérivées", | ||||
|                         fields = sheet.subCharacteristics.map { | ||||
|                             FieldUio.create( | ||||
|                                 label = it.label, | ||||
|                                 initialValue = it.value, | ||||
|                             ) | ||||
|                         }, | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Compétances", | ||||
|                         fields = sheet.skills.map { | ||||
|                             FieldUio.create( | ||||
|                                 label = it.label, | ||||
|                                 initialValue = "${it.value}", | ||||
|                             ) | ||||
|                         }, | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Occupations", | ||||
|                         editable = true, | ||||
|                         fields = sheet.occupations.map { | ||||
|                             FieldUio.create( | ||||
|                                 label = it.label, | ||||
|                                 initialValue = "${it.value}", | ||||
|                             ) | ||||
|                         }, | ||||
|                     ), | ||||
|                     Group( | ||||
|                         title = "Compétences magiques", | ||||
|                         editable = true, | ||||
|                         fields = sheet.magics.map { | ||||
|                             FieldUio.create( | ||||
|                                 label = it.label, | ||||
|                                 initialValue = "${it.value}", | ||||
|                             ) | ||||
|                         }, | ||||
|                     ), | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| private fun trunc(value: Int): String { | ||||
|     return "${(truncate(value.toFloat() / 5f) * 5f).toInt()}" | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun CharacterSheetEdit( | ||||
|     form: CharacterSheetEditUio, | ||||
|     onSkill: (CharacterSheetEditUio.Group) -> Unit, | ||||
| ) { | ||||
|     Column( | ||||
|         modifier = Modifier | ||||
|             .verticalScroll(state = rememberScrollState()) | ||||
|             .padding(all = 24.dp), | ||||
|         verticalArrangement = Arrangement.spacedBy(space = 16.dp) | ||||
|     ) { | ||||
|         Form( | ||||
|             modifier = Modifier.fillMaxWidth(), | ||||
|             field = form.name, | ||||
|         ) | ||||
| 
 | ||||
|         form.groups.forEach { | ||||
|             DecoratedBox( | ||||
|                 modifier = Modifier.animateContentSize(), | ||||
|             ) { | ||||
|                 Column( | ||||
|                     horizontalAlignment = Alignment.CenterHorizontally, | ||||
|                 ) { | ||||
|                     Text( | ||||
|                         modifier = Modifier.padding(vertical = 8.dp), | ||||
|                         style = MaterialTheme.typography.caption, | ||||
|                         text = it.title, | ||||
|                     ) | ||||
|                     it.fields.forEach { | ||||
|                         Form( | ||||
|                             modifier = Modifier.fillMaxWidth(), | ||||
|                             field = it, | ||||
|                         ) | ||||
|                     } | ||||
|                     if (it.editable) { | ||||
|                         Row( | ||||
|                             modifier = Modifier.fillMaxWidth(), | ||||
|                             verticalAlignment = Alignment.CenterVertically, | ||||
|                             horizontalArrangement = Arrangement.spacedBy( | ||||
|                                 space = 8.dp, | ||||
|                                 alignment = Alignment.End | ||||
|                             ) | ||||
|                         ) { | ||||
|                             Button( | ||||
|                                 colors = ButtonDefaults.textButtonColors(), | ||||
|                                 onClick = { onSkill(it) }, | ||||
|                             ) { | ||||
|                                 Row( | ||||
|                                     verticalAlignment = Alignment.CenterVertically, | ||||
|                                     horizontalArrangement = Arrangement.spacedBy(space = 4.dp), | ||||
|                                 ) { | ||||
|                                     Text( | ||||
|                                         style = MaterialTheme.typography.caption, | ||||
|                                         text = "Ajouter une ligne", | ||||
|                                     ) | ||||
|                                     Icon( | ||||
|                                         imageVector = Icons.Default.Add, | ||||
|                                         contentDescription = null, | ||||
|                                     ) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,104 @@ | |||
| package com.pixelized.desktop.lwa.screen.characterSheet.edit | ||||
| 
 | ||||
| import androidx.compose.animation.AnimatedContent | ||||
| import androidx.compose.animation.fadeIn | ||||
| import androidx.compose.animation.fadeOut | ||||
| import androidx.compose.animation.togetherWith | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.foundation.text.KeyboardActions | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.material.TextField | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.Stable | ||||
| import androidx.compose.runtime.State | ||||
| import androidx.compose.runtime.derivedStateOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.focus.FocusDirection | ||||
| import androidx.compose.ui.platform.LocalFocusManager | ||||
| import androidx.compose.ui.unit.dp | ||||
| 
 | ||||
| @Stable | ||||
| open class FieldUio( | ||||
|     val useLabelAsPlaceholder: Boolean, | ||||
|     val label: State<String>, | ||||
|     val onLabelChange: (String) -> Unit, | ||||
|     val valuePlaceHolder: State<String>, | ||||
|     val value: State<String>, | ||||
|     val onValueChange: (String) -> Unit, | ||||
| ) { | ||||
|     companion object { | ||||
|         @Stable | ||||
|         fun create( | ||||
|             useLabelAsPlaceholder: Boolean = false, | ||||
|             label: String = "", | ||||
|             initialValue: String = "", | ||||
|             valuePlaceHolder: () -> String = { "" }, | ||||
|         ): FieldUio { | ||||
|             val labelState = mutableStateOf(label) | ||||
|             val valueState = mutableStateOf(initialValue) | ||||
|             return FieldUio( | ||||
|                 useLabelAsPlaceholder = useLabelAsPlaceholder, | ||||
|                 label = labelState, | ||||
|                 onLabelChange = { labelState.value = it }, | ||||
|                 valuePlaceHolder = derivedStateOf(valuePlaceHolder), | ||||
|                 value = valueState, | ||||
|                 onValueChange = { valueState.value = it }, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun Form( | ||||
|     modifier: Modifier = Modifier, | ||||
|     field: FieldUio, | ||||
| ) { | ||||
|     val focus = LocalFocusManager.current | ||||
| 
 | ||||
|     AnimatedContent( | ||||
|         targetState = field.useLabelAsPlaceholder, | ||||
|         transitionSpec = { fadeIn() togetherWith fadeOut() } | ||||
|     ) { | ||||
|         when (it) { | ||||
|             true -> { | ||||
|                 TextField( | ||||
|                     modifier = modifier, | ||||
|                     value = field.value.value, | ||||
|                     label = { Text(text = field.label.value) }, | ||||
|                     singleLine = true, | ||||
|                     keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) }, | ||||
|                     onValueChange = field.onValueChange, | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             else -> { | ||||
|                 Row( | ||||
|                     modifier = modifier, | ||||
|                     horizontalArrangement = Arrangement.spacedBy(space = 8.dp), | ||||
|                     verticalAlignment = Alignment.CenterVertically, | ||||
|                 ) { | ||||
|                     TextField( | ||||
|                         modifier = Modifier.weight(weight = 1f), | ||||
|                         value = field.label.value, | ||||
|                         placeholder = { Text(text = "Nom") }, | ||||
|                         singleLine = true, | ||||
|                         keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) }, | ||||
|                         onValueChange = field.onLabelChange, | ||||
|                     ) | ||||
|                     TextField( | ||||
|                         modifier = Modifier.width(width = 80.dp), | ||||
|                         value = field.value.value, | ||||
|                         placeholder = { Text(text = field.valuePlaceHolder.value) }, | ||||
|                         singleLine = true, | ||||
|                         keyboardActions = KeyboardActions { focus.moveFocus(FocusDirection.Next) }, | ||||
|                         onValueChange = field.onValueChange, | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue