From b71645a7a23054209cc3cc85c750b5a26e265d02 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 5 Nov 2024 14:16:57 +0100 Subject: [PATCH] Link the CharacterSheet model to the UI. --- composeApp/characterssheet.preferences_pb | Bin 2441 -> 2649 bytes .../kotlin/com/pixelized/desktop/lwa/App.kt | 21 + .../composable/decoratedBox/DecoratedBox.kt | 0 .../lwa/composable/overlay/BlurOverlay.kt | 0 .../overlay/BlurOverlayViewModel.kt | 0 .../desktop/lwa/navigation/MainNavHost.kt | 2 + .../destination/CharacterSheetDestination.kt | 48 +++ .../CharacterSheetEditDestination.kt | 48 +++ .../navigation/destination/MainDestination.kt | 0 .../CharacterSheetRepository.kt | 29 +- .../detail/CharacterSheetFactory.kt | 70 ++++ .../detail/CharacterSheetPage.kt | 377 ++++++++++++++++++ .../detail/CharacterSheetViewModel.kt | 32 ++ .../edit/CharacterSheetEditPage.kt | 196 +++++++++ .../edit/CharacterSheetEditViewModel.kt | 64 +++ .../edit/CharacterSheetFactory.kt} | 223 ++++++----- .../edit/composable/FieldUio.kt | 0 .../desktop/lwa/screen/main/MainPage.kt | 95 +++++ .../lwa/screen/main/MainPageViewModel.kt | 8 +- .../desktop/lwa/screen/roll/RollPage.kt | 0 .../desktop/lwa/screen/roll/RollViewModel.kt | 2 +- .../pixelized/desktop/lwa/theme/LwaTheme.kt | 0 .../desktop/lwa/utils/extention/StringExt.kt | 3 + .../kotlin/com/pixelized/desktop/lwa/App.kt | 143 ------- .../CharacterSheetEditDestination.kt | 27 -- .../characterSheet/CharacterSheetPage.kt | 335 ---------------- .../characterSheet/CharacterSheetViewModel.kt | 19 - .../edit/CharacterSheetEditPage.kt | 166 -------- .../edit/CharacterSheetFactory.kt | 58 --- .../desktop/lwa/screen/main/MainPage.kt | 108 ----- 30 files changed, 1113 insertions(+), 961 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/composable/decoratedBox/DecoratedBox.kt (100%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/composable/overlay/BlurOverlay.kt (100%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/composable/overlay/BlurOverlayViewModel.kt (100%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/navigation/MainNavHost.kt (89%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetDestination.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/navigation/destination/MainDestination.kt (100%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetViewModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt rename composeApp/src/{desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt => commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt} (50%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/FieldUio.kt (100%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt (87%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt (100%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt (97%) rename composeApp/src/{desktopMain => commonMain}/kotlin/com/pixelized/desktop/lwa/theme/LwaTheme.kt (100%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/StringExt.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/App.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetPage.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetViewModel.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt diff --git a/composeApp/characterssheet.preferences_pb b/composeApp/characterssheet.preferences_pb index fd03c564f3d8b8de2497f3b2e8c0b36d8535c806..a05cb135e44d11a626ad004cd22ebf0a090fa659 100644 GIT binary patch literal 2649 zcmd#U)zrH3e*z&z{c2!05xko|==Io0?Zr!XV@W(yO17n3t~apOlrFTw+~O zP+Y{|l$@WdSCCnenv+?TnxdDITAW>yU!a#$o~T!pT999yS(0B=sh6CQSd^Gtl3Em; zk(yef=ZwPjFnyj}Y$R&T#31a+z?xW;n_uL~zyZ>dS)7~b$-tAGpI2OxSyBpettSI# za%oXfYF>#)0fHS6;K{(5l3G!cT9jE*>B+#Bk(!yFQR2zKlbKhNnv;{6o|>1O>dC;G zn^*zT2ckiGICAsLz#jBuU@gcmPX$?ATvC*pmtKn8w_G1Mm z29e>FV3;yfKoP)#l607nk`4=I(qZ;tKuS8y;Lrvo9(D!>27U$x20kdw&A`CG%fP_E zi6q7amE$5Y=`b6nn3OQ67#XFR7$g}Q>87Tbndq9BTPErznwc5srllApo2MlkTbd=M zfRh!-J&fQKz*tRwLC`g~Ah9GPGgU#uF)uSWF-H?cnmr)1xFj(h zRS&mIW^r=S;g#?*0BnIq34@GteqLT;W^r+1UUI5}hDT~qQhrfpF{o%uMKxF0v8W`o zI597=L_xzpFTFH1uSBx~Y%|F50$|4rfcapvy-OI_of6X%i;7TP!tPpJSejXeBq##% zW@2VBvNu6uJ|HI*AxtzXVc>Qw$;biuqBs}H0F4p`PRHb;{G`MZWHZc57`Osbi;`0d zP<<^~!oV4nnw*hZl$?QN3V#U$2P|X}mWn_ODK1Sc2Gw4mC6mx2;E3?&TM;|650YjJX7Za!*cij*+$1r(*` zW~LSuD-`Ev<`qLCfr){EfsuiM0hSX%xetUvxsC^#6G40s2IW)`<_39(y@Ual!xB@> zQxnY$b5T;Z!x!XSw~?dPWA zOV^>`=p{E@Gn6o}p{Hv%TJ zajqo{D#=Evre+4FX1Ycx7UsGpMyBSvNoj`0x@iU`riK=V7G|mDW`zuO4D2NgY~J}r zm5Ie*9+&_#nI<1(H1PxTOBfgpN*Gkk&5g}W)6C6vjm=XHbxl&!EOaf+%`J6}Q&Lh4 zQxnt7%~MG=GTFk&!py|NOgGuU0A!?bif*EXsikg;MUsiJiJ57diKzwD33yyt4^;wo zrA2C*k)?@=rLMU_lCiFdr9qOeg=tc%u4$riN>XaFsbQ)`DpWlVBN-rooq>UYpMima zkAZ=Kmw|zS8%lE`iE)9|m@+VML5*ZKOfe~8P%%ugurxAEOV&*Sg_lX1nVGIdT56iE zk!4bfnPHl_iJ77CDEXgd+MODn{m|T>flvsjnhIt7CS3qh}a%w?I zX1+-t!XSnc2F{?= { @@ -28,6 +29,7 @@ fun MainNavHost( ) { composableMainPage() + composableCharacterSheetPage() composableCharacterSheetEditPage() } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetDestination.kt new file mode 100644 index 0000000..6e35273 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetDestination.kt @@ -0,0 +1,48 @@ +package com.pixelized.desktop.lwa.navigation.destination + +import androidx.lifecycle.SavedStateHandle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import androidx.navigation.navArgument +import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPage +import com.pixelized.desktop.lwa.utils.extention.ARG + +object CharacterSheetDestination { + private const val ROUTE = "character.sheet" + private const val CHARACTER_ID = "id" + + fun baseRoute() = "$ROUTE?${CHARACTER_ID.ARG}" + + fun navigationRoute(id: String) = "$ROUTE?$CHARACTER_ID=$id" + + fun arguments() = listOf( + navArgument(CHARACTER_ID) { + nullable = true + } + ) + + data class Argument( + val id: String, + ) { + constructor(savedStateHandle: SavedStateHandle) : this( + id = savedStateHandle.get(CHARACTER_ID) ?: error("missing character id") + ) + } +} + +fun NavGraphBuilder.composableCharacterSheetPage() { + composable( + route = CharacterSheetDestination.baseRoute(), + arguments = CharacterSheetDestination.arguments(), + ) { + CharacterSheetPage() + } +} + +fun NavHostController.navigateToCharacterSheet( + id: String, +) { + val route = CharacterSheetDestination.navigationRoute(id = id) + navigate(route = route) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt new file mode 100644 index 0000000..fe069af --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt @@ -0,0 +1,48 @@ +package com.pixelized.desktop.lwa.navigation.destination + +import androidx.lifecycle.SavedStateHandle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import androidx.navigation.navArgument +import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPage +import com.pixelized.desktop.lwa.utils.extention.ARG + +object CharacterSheetEditDestination { + private const val ROUTE = "character.sheet.edit" + private const val CHARACTER_ID = "id" + + fun baseRoute() = "$ROUTE?${CHARACTER_ID.ARG}" + + fun navigationRoute(id: String?) = "$ROUTE?$CHARACTER_ID=$id" + + fun arguments() = listOf( + navArgument(CHARACTER_ID) { + nullable = true + } + ) + + data class Argument( + val id: String?, + ) { + constructor(savedStateHandle: SavedStateHandle) : this( + id = savedStateHandle.get(CHARACTER_ID) + ) + } +} + +fun NavGraphBuilder.composableCharacterSheetEditPage() { + composable( + route = CharacterSheetEditDestination.baseRoute(), + arguments = CharacterSheetEditDestination.arguments(), + ) { + CharacterSheetEditPage() + } +} + +fun NavHostController.navigateToCharacterSheetEdit( + id: String? = null, +) { + val route = CharacterSheetEditDestination.navigationRoute(id = id) + navigate(route = route) +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/MainDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/MainDestination.kt similarity index 100% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/MainDestination.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/MainDestination.kt diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetRepository.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetRepository.kt index 4a8e5b0..711c9be 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/characterSheet/CharacterSheetRepository.kt @@ -10,28 +10,31 @@ import kotlinx.coroutines.flow.stateIn object CharacterSheetRepository { private val scope = CoroutineScope(Dispatchers.IO) + private val preferences = CharacterSheetPreference( dataStore = createDataStore { "characterssheet.preferences_pb" } ) - fun characterSheet(): StateFlow> { - return preferences.loadFlow() - .stateIn( - scope = scope, - started = SharingStarted.Eagerly, - initialValue = emptyList() - ) + private val sheets = preferences.loadFlow() + .stateIn( + scope = scope, + started = SharingStarted.Eagerly, + initialValue = emptyList() + ) + + fun characterSheetFlow(): StateFlow> { + return sheets } - fun characterSheet(id: String): StateFlow { - return preferences.loadFlow() + fun characterSheetFlow(id: String?): StateFlow { + return sheets .map { sheets -> sheets.firstOrNull { sheet -> sheet.id == id } } .stateIn( scope = scope, started = SharingStarted.Eagerly, - initialValue = null + initialValue = sheets.value.firstOrNull { it.id == id } ) } @@ -48,5 +51,11 @@ object CharacterSheetRepository { // save the list of characters sheet. preferences.save(sheets = savedSheets) } + + suspend fun delete(id: String) { + val savedSheets = preferences.load().toMutableList() + savedSheets.removeIf { it.id == id } + preferences.save(sheets = savedSheets) + } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt new file mode 100644 index 0000000..931b3d4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetFactory.kt @@ -0,0 +1,70 @@ +package com.pixelized.desktop.lwa.screen.characterSheet.detail + +import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet +import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic +import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio.Node + +class CharacterSheetFactory { + + fun convertToUio(model: CharacterSheet): CharacterSheetPageUio { + return CharacterSheetPageUio( + id = model.id, + name = model.name, + characteristics = listOf( + Characteristic(label = "Force", value = "${model.strength}"), + Characteristic(label = "Dextérité", value = "${model.dexterity}"), + Characteristic(label = "Constitution", value = "${model.constitution}"), + Characteristic(label = "Taille", value = "${model.height}"), + Characteristic(label = "Intelligence", value = "${model.intelligence}"), + Characteristic(label = "Pouvoir", value = "${model.power}"), + Characteristic(label = "Charisme", value = "${model.charisma}"), + ), + subCharacteristics = listOf( + Characteristic(label = "Déplacement ", value = "${model.movement}"), + Characteristic( + label = "Points de vie", + value = "${model.currentHp}/${model.maxHp}" + ), + Characteristic( + label = "Points de pouvoir", + value = "${model.currentPP}/${model.maxPP}" + ), + Characteristic(label = "Bonus aux dégâts", value = model.damageBonus), + Characteristic(label = "Armure", value = "${model.armor}"), + ), + skills = model.skills.mapNotNull { + if (it.value > 0) { + Node( + type = Node.Type.SKILLS, + label = it.label, + value = it.value, + ) + } else { + null + } + }, + occupations = model.occupations.mapNotNull { + if (it.value > 0) { + Node( + type = Node.Type.OCCUPATIONS, + label = it.label, + value = it.value, + ) + } else { + null + } + }, + magics = model.magics.mapNotNull { + if (it.value > 0) { + Node( + type = Node.Type.MAGICS, + label = it.label, + value = it.value, + ) + } else { + null + } + }, + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt new file mode 100644 index 0000000..788abc8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetPage.kt @@ -0,0 +1,377 @@ +package com.pixelized.desktop.lwa.screen.characterSheet.detail + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Checkbox +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Edit +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.compose.viewModel +import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox +import com.pixelized.desktop.lwa.composable.overlay.BlurOverlay +import com.pixelized.desktop.lwa.composable.overlay.BlurOverlayViewModel +import com.pixelized.desktop.lwa.navigation.LocalScreen +import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit +import com.pixelized.desktop.lwa.screen.roll.RollPage +import com.pixelized.desktop.lwa.screen.roll.RollViewModel +import kotlinx.coroutines.launch + +@Stable +data class CharacterSheetPageUio( + val id: String, + val name: String, + val characteristics: List, + val subCharacteristics: List, + val skills: List, + val occupations: List, + val magics: List, +) { + @Stable + data class Characteristic( + val label: String, + val value: String, + ) + + @Stable + data class Node( + val type: Type, + val label: String, + val value: Int, + ) { + @Stable + enum class Type { + SKILLS, OCCUPATIONS, MAGICS, + } + } +} + +@Composable +fun CharacterSheetPage( + viewModel: CharacterSheetViewModel = viewModel { + CharacterSheetViewModel(savedStateHandle = createSavedStateHandle()) + }, + overlayViewModel: BlurOverlayViewModel = viewModel { BlurOverlayViewModel() }, + rollViewModel: RollViewModel = viewModel { RollViewModel() }, +) { + val screen = LocalScreen.current + val scope = rememberCoroutineScope() + + Surface( + modifier = Modifier.fillMaxSize(), + ) { + BlurOverlay( + viewModel = overlayViewModel, + overlay = { + RollPage( + viewModel = rollViewModel, + onDismissRequest = overlayViewModel::hide, + ) + }, + content = { + viewModel.sheet.value?.let { + CharacterSheetPageContent( + modifier = Modifier.fillMaxSize(), + characterSheet = it, + onBack = { + screen.popBackStack() + }, + onEdit = { + screen.navigateToCharacterSheetEdit(id = it.id) + }, + onDelete = { + scope.launch { + viewModel.deleteCharacter(id = it.id) + screen.popBackStack() + } + }, + onCharacteristic = { characteristic -> + rollViewModel.prepareRoll(characteristic = characteristic) + overlayViewModel.show() + }, + onSkill = { node -> + rollViewModel.prepareRoll(node = node) + overlayViewModel.show() + }, + ) + } + }, + ) + } +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun CharacterSheetPageContent( + modifier: Modifier = Modifier, + scrollState: ScrollState = rememberScrollState(), + width: Dp = 320.dp, + characterSheet: CharacterSheetPageUio, + onBack: () -> Unit, + onEdit: () -> Unit, + onDelete: () -> Unit, + onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit, + onSkill: (skill: CharacterSheetPageUio.Node) -> Unit, +) { + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = characterSheet.name, + ) + }, + actions = { + IconButton( + onClick = onEdit, + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = null, + ) + } + IconButton( + onClick = onDelete, + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = null, + ) + } + }, + navigationIcon = { + IconButton( + onClick = onBack, + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null, + ) + } + } + ) + }, + content = { paddingValues -> + Column( + modifier = Modifier + .verticalScroll(state = scrollState).padding(all = 16.dp) + .padding(paddingValues) + .then(other = modifier), + verticalArrangement = Arrangement.spacedBy(space = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + FlowRow( + maxItemsInEachRow = 3, + horizontalArrangement = Arrangement.spacedBy( + space = 16.dp, + alignment = Alignment.CenterHorizontally, + ), + verticalArrangement = Arrangement.spacedBy(space = 16.dp), + ) { + characterSheet.characteristics.forEach { + Stat( + modifier = Modifier.width(width = width / 3 - 32.dp) + .height(height = 112.dp), + characteristic = it, + onClick = { onCharacteristic(it) }, + ) + } + } + DecoratedBox( + modifier = Modifier.width(width = width).padding(vertical = 8.dp), + ) { + Column { + Text( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + text = "Charactéristiques dérivées" + ) + characterSheet.subCharacteristics.forEach { + Characteristics( + modifier = Modifier.fillMaxWidth(), + characteristic = it, + ) + } + } + } + DecoratedBox( + modifier = Modifier.width(width = width).padding(vertical = 8.dp), + ) { + Column { + Text( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + text = "Compétences", + ) + characterSheet.skills.forEach { + Skill( + modifier = Modifier.fillMaxWidth(), + label = it.label, + value = it.value, + onClick = { onSkill(it) }, + ) + } + } + } + DecoratedBox( + modifier = Modifier.width(width = width).padding(vertical = 8.dp), + ) { + Column { + Text( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + text = "Occupations" + ) + characterSheet.occupations.forEach { + Skill( + modifier = Modifier.fillMaxWidth(), + label = it.label, + value = it.value, + onClick = { onSkill(it) }, + ) + } + } + } + DecoratedBox( + modifier = Modifier.width(width = width).padding(vertical = 8.dp), + ) { + Column { + Text( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + text = "Compétences magiques" + ) + characterSheet.magics.forEach { + Skill( + modifier = Modifier.fillMaxWidth(), + label = it.label, + value = it.value, + onClick = { onSkill(it) }, + ) + } + } + } + } + } + ) +} + +@Composable +private fun Stat( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues(all = 8.dp), + characteristic: CharacterSheetPageUio.Characteristic, + onClick: () -> Unit, +) { + DecoratedBox( + modifier = Modifier.clickable(onClick = onClick).padding(paddingValues = paddingValues) + .then(other = modifier), + ) { + Text( + modifier = Modifier.align(alignment = Alignment.TopCenter), + style = MaterialTheme.typography.caption, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = characteristic.label, + ) + Text( + modifier = Modifier.align(alignment = Alignment.Center), + style = MaterialTheme.typography.h4, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = characteristic.value + ) + } +} + +@Composable +private fun Characteristics( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp), + characteristic: CharacterSheetPageUio.Characteristic, +) { + Row( + modifier = Modifier.padding(paddingValues = paddingValues).then(other = modifier), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.body1, + text = characteristic.label + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + text = characteristic.value, + ) + } +} + +@Composable +private fun Skill( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp), + label: String, + value: Any, + onClick: () -> Unit, +) { + Row( + modifier = Modifier.clickable(onClick = onClick).padding(paddingValues = paddingValues) + .then(other = modifier), + horizontalArrangement = Arrangement.spacedBy(space = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.body1, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = label + ) + Text( + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Bold, + text = "$value", + ) + Checkbox(modifier = Modifier.size(size = 32.dp), checked = false, onCheckedChange = { }) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetViewModel.kt new file mode 100644 index 0000000..11ec376 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/detail/CharacterSheetViewModel.kt @@ -0,0 +1,32 @@ +package com.pixelized.desktop.lwa.screen.characterSheet.detail + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination +import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository +import com.pixelized.desktop.lwa.utils.extention.collectAsState + +class CharacterSheetViewModel( + savedStateHandle: SavedStateHandle, +) : ViewModel() { + + private val argument = CharacterSheetDestination.Argument(savedStateHandle) + private val repository = CharacterSheetRepository + private val factory = CharacterSheetFactory() + + val sheet: State + @Composable + @Stable + get() = repository + .characterSheetFlow(id = argument.id) + .collectAsState { sheet -> + sheet?.let { model -> factory.convertToUio(model = model) } + } + + suspend fun deleteCharacter(id: String) { + repository.delete(id = id) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt new file mode 100644 index 0000000..4ed19f8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt @@ -0,0 +1,196 @@ +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.fillMaxSize +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.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.compose.viewModel +import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox +import com.pixelized.desktop.lwa.navigation.LocalScreen +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.Form +import kotlinx.coroutines.launch + +@Stable +data class CharacterSheetEditPageUio( + val id: String, + val name: FieldUio, + val skills: List, +) { + @Stable + data class SkillGroup( + val title: String, + val type: Type, + val editable: Boolean = false, + val fields: List, + ) { + @Stable + enum class Type { + CHARACTERISTICS, + SUB_CHARACTERISTICS, + SKILLS, + OCCUPATIONS, + MAGICS, + OTHER, + } + } +} + + +@Composable +fun CharacterSheetEditPage( + viewModel: CharacterSheetEditViewModel = viewModel { + CharacterSheetEditViewModel( + savedStateHandle = createSavedStateHandle() + ) + }, +) { + val screen = LocalScreen.current + val scope = rememberCoroutineScope() + + Surface( + modifier = Modifier.fillMaxSize(), + ) { + CharacterSheetEdit( + form = viewModel.characterSheet.value, + onSkill = viewModel::onSkill, + onBack = { screen.popBackStack() }, + onSave = { + scope.launch { + viewModel.save() + screen.popBackStack() + } + }, + ) + } +} + +@Composable +fun CharacterSheetEdit( + form: CharacterSheetEditPageUio, + onSkill: (CharacterSheetEditPageUio.SkillGroup) -> Unit, + onBack: () -> Unit, + onSave: () -> Unit, +) { + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = "Création de personnage", + ) + }, + navigationIcon = { + IconButton( + onClick = onBack, + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null, + ) + } + } + ) + }, + content = { paddingValues -> + Column( + modifier = Modifier + .verticalScroll(state = rememberScrollState()) + .padding(paddingValues = paddingValues) + .padding(all = 24.dp), + verticalArrangement = Arrangement.spacedBy(space = 16.dp) + ) { + Form( + modifier = Modifier.fillMaxWidth(), + field = form.name, + ) + + form.skills.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 + ) + ) { + TextButton( + onClick = { onSkill(it) }, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = 4.dp), + ) { + Text( + text = "Ajouter une ligne", + ) + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + ) + } + } + } + } + } + } + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + TextButton( + onClick = onSave, + ) { + Text(text = "Sauvegarder") + } + } + } + } + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt new file mode 100644 index 0000000..3f9e2f4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt @@ -0,0 +1,64 @@ +package com.pixelized.desktop.lwa.screen.characterSheet.edit + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetEditDestination +import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository +import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPageUio.SkillGroup +import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio + +class CharacterSheetEditViewModel( + savedStateHandle: SavedStateHandle, +) : ViewModel() { + + private val argument = CharacterSheetEditDestination.Argument(savedStateHandle) + private val repository = CharacterSheetRepository + private val factory = CharacterSheetFactory() + + private val _characterSheet = repository + .characterSheetFlow(id = argument.id).value + .let { sheet -> mutableStateOf(factory.convertToUio(sheet = sheet)) } + val characterSheet: State get() = _characterSheet + + fun onSkill(skill: SkillGroup) { + val sheet = _characterSheet.value + + _characterSheet.value = sheet.copy( + skills = sheet.skills.map { group -> + if (skill.title == group.title) { + group.copy( + fields = mutableListOf().apply { + addAll(group.fields) + add( + FieldUio.create( + label = "", + isLabelEditable = true, + valuePlaceHolder = { + when (group.type) { + SkillGroup.Type.CHARACTERISTICS -> "" + SkillGroup.Type.SUB_CHARACTERISTICS -> "" + SkillGroup.Type.SKILLS -> "0" + SkillGroup.Type.OCCUPATIONS -> "40" + SkillGroup.Type.MAGICS -> "0" + SkillGroup.Type.OTHER -> "" + } + }, + ) + ) + } + ) + } else { + group + } + } + ) + } + + suspend fun save() { + val sheet = _characterSheet.value + val model = factory.convertToModel(sheet = sheet) + repository.save(characterSheet = model) + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt similarity index 50% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt index 51ddb98..ab2eb16 100644 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt @@ -1,96 +1,116 @@ package com.pixelized.desktop.lwa.screen.characterSheet.edit -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel import com.pixelized.desktop.lwa.business.SkillNormalizerUseCase.normalize -import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository +import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPageUio.SkillGroup import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio import java.util.UUID import kotlin.math.max -class CharacterSheetEditViewModel : ViewModel() { +class CharacterSheetFactory { - private val repository = CharacterSheetRepository - private val factory = CharacterSheetFactory() - - private val _characterSheet: MutableState - val characterSheet: State get() = _characterSheet - - init { - _characterSheet = mutableStateOf(createEmptyCharacterSheet()) - } - - fun onSkill(skill: SkillGroup) { - val sheet = _characterSheet.value - - _characterSheet.value = sheet.copy( - skills = sheet.skills.map { group -> - if (skill.title == group.title) { - group.copy( - fields = mutableListOf().apply { - addAll(group.fields) - add( - FieldUio.create( - label = "", - isLabelEditable = true, - valuePlaceHolder = { - when (group.type) { - SkillGroup.Type.CHARACTERISTICS -> "" - SkillGroup.Type.SUB_CHARACTERISTICS -> "" - SkillGroup.Type.SKILLS -> "0" - SkillGroup.Type.OCCUPATIONS -> "40" - SkillGroup.Type.MAGICS -> "0" - SkillGroup.Type.OTHER -> "" - } - }, - ) - ) - } - ) - } else { - group - } - } + fun convertToModel(sheet: CharacterSheetEditPageUio): CharacterSheet { + return CharacterSheet( + id = sheet.id, + name = sheet.name.value.value, + strength = sheet.skills[0].fields[0].unpack(), + dexterity = sheet.skills[0].fields[1].unpack(), + constitution = sheet.skills[0].fields[2].unpack(), + height = sheet.skills[0].fields[3].unpack(), + intelligence = sheet.skills[0].fields[4].unpack(), + power = sheet.skills[0].fields[5].unpack(), + charisma = sheet.skills[0].fields[6].unpack(), + movement = sheet.skills[1].fields[0].unpack(), + currentHp = sheet.skills[1].fields[1].unpack(), + maxHp = sheet.skills[1].fields[1].unpack(), + currentPP = sheet.skills[1].fields[2].unpack(), + maxPP = sheet.skills[1].fields[2].unpack(), + damageBonus = sheet.skills[1].fields[3].unpack(), + armor = sheet.skills[1].fields[4].unpack(), + skills = sheet.skills[2].fields.map { + CharacterSheet.Skill( + label = it.label.value, + value = it.unpack(), + used = false, + ) + }, + occupations = sheet.skills[3].fields.map { + CharacterSheet.Skill( + label = it.label.value, + value = it.unpack(), + used = false, + ) + }, + magics = sheet.skills[4].fields.map { + CharacterSheet.Skill( + label = it.label.value, + value = it.unpack(), + used = false, + ) + }, + attacks = emptyList(), ) } - suspend fun save() { - val sheet = _characterSheet.value - val model = factory.convertToModel(sheet = sheet) - repository.save(characterSheet = model) - } + fun convertToUio( + sheet: CharacterSheet?, + ): CharacterSheetEditPageUio { + val str = FieldUio.create( + label = "Force", + valuePlaceHolder = { "0" }, + initialValue = sheet?.strength?.toString() ?: "" + ) + val dex = FieldUio.create( + label = "Dextérité", + valuePlaceHolder = { "0" }, + initialValue = sheet?.dexterity?.toString() ?: "" + ) + val con = FieldUio.create( + label = "Constitution", + valuePlaceHolder = { "0" }, + initialValue = sheet?.constitution?.toString() ?: "" + ) + val hei = FieldUio.create( + label = "Taille", + valuePlaceHolder = { "0" }, + initialValue = sheet?.height?.toString() ?: "" + ) + val int = FieldUio.create( + label = "Intelligence", + valuePlaceHolder = { "0" }, + initialValue = sheet?.intelligence?.toString() ?: "" + ) + val pow = FieldUio.create( + label = "Pouvoir", + valuePlaceHolder = { "0" }, + initialValue = sheet?.power?.toString() ?: "" + ) + val cha = FieldUio.create( + label = "Charisme", + valuePlaceHolder = { "0" }, + initialValue = sheet?.charisma?.toString() ?: "" + ) - private fun createEmptyCharacterSheet(): CharacterSheetEditPageUio { - val str = FieldUio.create(label = "Force", valuePlaceHolder = { "0" }) - val dex = FieldUio.create(label = "Dextérité", valuePlaceHolder = { "0" }) - val con = FieldUio.create(label = "Constitution", valuePlaceHolder = { "0" }) - val vit = FieldUio.create(label = "Taille", valuePlaceHolder = { "0" }) - val int = FieldUio.create(label = "Intelligence", valuePlaceHolder = { "0" }) - val pow = FieldUio.create(label = "Pouvoir", valuePlaceHolder = { "0" }) - val cha = FieldUio.create(label = "Charisme", valuePlaceHolder = { "0" }) - - fun str(): Int = str.value.value.toIntOrNull() ?: 0 - fun dex(): Int = dex.value.value.toIntOrNull() ?: 0 - fun con(): Int = con.value.value.toIntOrNull() ?: 0 - fun vit(): Int = vit.value.value.toIntOrNull() ?: 0 - fun int(): Int = int.value.value.toIntOrNull() ?: 0 - fun pow(): Int = pow.value.value.toIntOrNull() ?: 0 - fun cha(): Int = cha.value.value.toIntOrNull() ?: 0 + fun str(): Int = str.unpack() ?: 0 + fun dex(): Int = dex.unpack() ?: 0 + fun con(): Int = con.unpack() ?: 0 + fun hei(): Int = hei.unpack() ?: 0 + fun int(): Int = int.unpack() ?: 0 + fun pow(): Int = pow.unpack() ?: 0 + fun cha(): Int = cha.unpack() ?: 0 return CharacterSheetEditPageUio( - id = UUID.randomUUID().toString(), + id = sheet?.id ?: UUID.randomUUID().toString(), name = FieldUio.create( useLabelAsPlaceholder = true, label = "Name", + initialValue = sheet?.name ?: "" ), skills = listOf( SkillGroup( title = "Charactéristiques", type = SkillGroup.Type.CHARACTERISTICS, - fields = listOf(str, dex, con, vit, int, pow, cha), + fields = listOf(str, dex, con, hei, int, pow, cha), ), SkillGroup( title = "Charactéristiques dérivées", @@ -99,19 +119,22 @@ class CharacterSheetEditViewModel : ViewModel() { FieldUio.create( label = "Déplacement", valuePlaceHolder = { "10" }, + initialValue = sheet?.movement?.toString() ?: "" ), FieldUio.create( label = "Points de vie", - valuePlaceHolder = { "${(con() + vit()) / 2}" }, + valuePlaceHolder = { "${(con() + hei()) / 2}" }, + initialValue = sheet?.maxHp?.toString() ?: "" ), FieldUio.create( label = "Points de pouvoir", valuePlaceHolder = { "${pow()}" }, + initialValue = sheet?.maxPP?.toString() ?: "" ), FieldUio.create( label = "Bonus aux dégats", valuePlaceHolder = { - val bonus = str() + vit() + val bonus = str() + hei() when { bonus < 12 -> "-1d6" bonus in 12..17 -> "-1d4" @@ -121,26 +144,37 @@ class CharacterSheetEditViewModel : ViewModel() { else -> "2d6" } }, + initialValue = sheet?.damageBonus ?: "" + ), + FieldUio.create( + label = "Armure", + valuePlaceHolder = { "0" }, + initialValue = sheet?.armor?.toString() ?: "" ), - FieldUio.create(label = "Armure", valuePlaceHolder = { "0" }), ), ), SkillGroup( title = "Compétances", type = SkillGroup.Type.SKILLS, editable = true, - fields = listOf( + fields = sheet?.skills?.map { + FieldUio.create( + label = it.label, + valuePlaceHolder = { "" }, + initialValue = it.value.toString(), + ) + } ?: listOf( FieldUio.create( label = "Bagarre", valuePlaceHolder = { "${normalize(dex() * 2)}" }, ), FieldUio.create( label = "Esquive", - valuePlaceHolder = { "${normalize(dex() * 2)}" } + valuePlaceHolder = { "${normalize(dex() * 2)}" }, ), FieldUio.create( label = "Saisie", - valuePlaceHolder = { "${normalize(str() + vit())}" }, + valuePlaceHolder = { "${normalize(str() + hei())}" }, ), FieldUio.create( label = "Lancer", @@ -172,16 +206,7 @@ class CharacterSheetEditViewModel : ViewModel() { ), FieldUio.create( label = "Intimidation", - valuePlaceHolder = { - "${ - normalize( - cha() + max( - pow(), - vit() - ) * 2 - ) - }" - }, + valuePlaceHolder = { "${normalize(cha() + max(pow(), hei()) * 2)}" }, ), FieldUio.create( label = "Baratin", @@ -193,7 +218,7 @@ class CharacterSheetEditViewModel : ViewModel() { ), FieldUio.create( label = "Discrétion", - valuePlaceHolder = { "${normalize(cha() + dex() * 2 - vit())}" }, + valuePlaceHolder = { "${normalize(cha() + dex() * 2 - hei())}" }, ), FieldUio.create( label = "Escamotage", @@ -209,15 +234,35 @@ class CharacterSheetEditViewModel : ViewModel() { title = "Occupations", type = SkillGroup.Type.OCCUPATIONS, editable = true, - fields = emptyList(), + fields = sheet?.occupations?.map { + FieldUio.create( + label = it.label, + valuePlaceHolder = { "40" }, + initialValue = it.value.toString() + ) + } ?: emptyList(), ), SkillGroup( title = "Compétences magiques", type = SkillGroup.Type.MAGICS, editable = true, - fields = emptyList(), + fields = sheet?.magics?.map { + FieldUio.create( + label = it.label, + valuePlaceHolder = { "" }, + initialValue = it.value.toString() + ) + } ?: emptyList(), ), ) ) } + + private inline fun FieldUio.unpack(): T { + val tmp = value.value.ifBlank { valuePlaceHolder.value } + return when (T::class) { + Int::class -> (tmp.toIntOrNull() ?: 0) as T + else -> tmp as T + } + } } \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/FieldUio.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/FieldUio.kt similarity index 100% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/FieldUio.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/composable/FieldUio.kt diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt new file mode 100644 index 0000000..6030c66 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt @@ -0,0 +1,95 @@ +package com.pixelized.desktop.lwa.screen.main + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.pixelized.desktop.lwa.navigation.LocalScreen +import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheet +import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit + +@Stable +data class CharacterUio( + val id: String, + val name: String, +) + +@Composable +fun MainPage( + viewModel: MainPageViewModel = viewModel { MainPageViewModel() }, +) { + val screen = LocalScreen.current + + Surface { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + MainPageContent( + characters = viewModel.characters, + onCharacter = { + screen.navigateToCharacterSheet(id = it.id) + }, + onCreateCharacter = { + screen.navigateToCharacterSheetEdit() + }, + ) + } + } +} + +@Composable +fun MainPageContent( + modifier: Modifier = Modifier, + characters: State>, + onCharacter: (CharacterUio) -> Unit, + onCreateCharacter: () -> Unit, +) { + Column( + modifier = modifier.padding(horizontal = 24.dp), + verticalArrangement = Arrangement.spacedBy(space = 32.dp), + ) { + Column { + characters.value.forEach { sheet -> + TextButton( + onClick = { onCharacter(sheet) } + ) { + Text( + modifier = Modifier.fillMaxWidth(), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Start, + text = sheet.name, + ) + } + } + } + + TextButton( + onClick = { onCreateCharacter() }, + ) { + Text( + modifier = Modifier.fillMaxWidth(), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Start, + text = "Créer une feuille de personnage", + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt similarity index 87% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt index 1936106..d627306 100644 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPageViewModel.kt @@ -14,8 +14,9 @@ class MainPageViewModel : ViewModel() { val characters: State> @Composable @Stable - get() { - return characterSheetRepository.characterSheet().collectAsState { sheets -> + get() = characterSheetRepository + .characterSheetFlow() + .collectAsState { sheets -> sheets.map { sheet -> CharacterUio( id = sheet.id, @@ -23,7 +24,4 @@ class MainPageViewModel : ViewModel() { ) } } - } - - } \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt similarity index 100% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt similarity index 97% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt index c7d22e5..2190e11 100644 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollViewModel.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import com.pixelized.desktop.lwa.business.RollUseCase import com.pixelized.desktop.lwa.business.SkillStepUseCase -import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetPageUio +import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/theme/LwaTheme.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/theme/LwaTheme.kt similarity index 100% rename from composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/theme/LwaTheme.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/theme/LwaTheme.kt diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/StringExt.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/StringExt.kt new file mode 100644 index 0000000..3975d9a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/utils/extention/StringExt.kt @@ -0,0 +1,3 @@ +package com.pixelized.desktop.lwa.utils.extention + +val String.ARG: String get() = "$this={$this}" \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/App.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/App.kt deleted file mode 100644 index ab43a65..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/App.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.pixelized.desktop.lwa - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.pixelized.desktop.lwa.navigation.MainNavHost -import com.pixelized.desktop.lwa.screen.main.MainPage -import com.pixelized.desktop.lwa.theme.LwaTheme -import org.jetbrains.compose.ui.tooling.preview.Preview - -@Composable -@Preview -fun App() { - LwaTheme { - Surface( - modifier = Modifier.fillMaxSize() - ) { - MainNavHost() - } - } -} - -// -// val sheetViewModel = viewModel { CharacterSheetViewModel() } -// val overlayViewModel = viewModel { BlurOverlayViewModel() } -// val rollViewModel = viewModel { RollViewModel() } -// -// val edit = remember { mutableStateOf(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 = { edit.value = CharacterSheetEditUio.Default }, -// ) { -// Text(text = "Créer une feuille de personnage") -// } -// } -// -// sheetViewModel.sheet.value?.let { sheet -> -// Window( -// onCloseRequest = sheetViewModel::hideCharacterSheet, -// state = rememberWindowState( -// width = 320.dp + 64.dp, -// height = 900.dp, -// ), -// title = "LwaCharacterSheet", -// ) { -// Surface( -// modifier = Modifier.fillMaxSize() -// ) { -// BlurOverlay( -// viewModel = overlayViewModel, -// overlay = { -// RollPage( -// viewModel = rollViewModel, -// onDismissRequest = overlayViewModel::hide, -// ) -// }, -// content = { -// CharacterSheet( -// modifier = Modifier.fillMaxWidth(), -// width = 320.dp, -// characterSheet = sheet, -// onCharacteristic = { characteristic -> -// rollViewModel.prepareRoll(characteristic = characteristic) -// overlayViewModel.show() -// }, -// onSkill = { node -> -// rollViewModel.prepareRoll(node = node) -// overlayViewModel.show() -// } -// ) -// }, -// ) -// } -// } -// } -// -// 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().apply { -// addAll(group.fields) -// add( -// FieldUio.create( -// label = "", -// valuePlaceHolder = { "40" }, -// ) -// ) -// } -// ) -// } else { -// group -// } -// } -// ) -// } -// ) -// } -// } -// } -// } -// } -//} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt deleted file mode 100644 index 20757f3..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/navigation/destination/CharacterSheetEditDestination.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pixelized.desktop.lwa.navigation.destination - -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable -import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEdit -import com.pixelized.desktop.lwa.screen.characterSheet.edit.CharacterSheetEditPage - -object CharacterSheetEditDestination { - private const val ROUTE = "character.sheet.edit" - - fun baseRoute() = ROUTE - fun navigationRoute() = ROUTE -} - -fun NavGraphBuilder.composableCharacterSheetEditPage() { - composable( - route = CharacterSheetEditDestination.baseRoute(), - ) { - CharacterSheetEditPage() - } -} - -fun NavHostController.navigateToCharacterSheetEdit() { - val route = CharacterSheetEditDestination.navigationRoute() - navigate(route = route) -} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetPage.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetPage.kt deleted file mode 100644 index 66fbce8..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetPage.kt +++ /dev/null @@ -1,335 +0,0 @@ -package com.pixelized.desktop.lwa.screen.characterSheet - -import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Checkbox -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -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.screen.characterSheet.CharacterSheetPageUio.Node.Type - -@Stable -data class CharacterSheetPageUio( - val name: String, - val characteristics: List, - val subCharacteristics: List, - val skills: List, - val occupations: List, - val magics: List, -) { - @Stable - data class Characteristic( - val label: String, - val value: String, - ) - - @Stable - data class Node( - val type: Type, - val label: String, - val value: Int, - ) { - @Stable - enum class Type { - SKILLS, - OCCUPATIONS, - MAGICS, - } - } - - companion object { - val Koryas = CharacterSheetPageUio( - name = "Koryas Tissenpa", - characteristics = listOf( - Characteristic(label = "Force", value = "10"), - Characteristic(label = "Dextérité", value = "11"), - Characteristic(label = "Constitution", value = "15"), - Characteristic(label = "Taille", value = "13"), - Characteristic(label = "Intelligence", value = "9"), - Characteristic(label = "Pouvoir", value = "15"), - Characteristic(label = "Charisme", value = "7"), - ), - subCharacteristics = listOf( - Characteristic(label = "Déplacement ", value = "10"), - Characteristic(label = "Points de vie", value = "14/14"), - Characteristic(label = "Points de pouvoir", value = "13/13"), - Characteristic(label = "Bonus aux dégâts", value = "1d4"), - Characteristic(label = "Armure", value = "0"), - ), - skills = listOf( - Node(type = Type.SKILLS, label = "Bagarre", value = 75), - Node(type = Type.SKILLS, label = "Esquive", value = 60), - Node(type = Type.SKILLS, label = "Saisie", value = 20), - Node(type = Type.SKILLS, label = "Lancer", value = 20), - Node(type = Type.SKILLS, label = "Athlétisme", value = 60), - Node(type = Type.SKILLS, label = "Acrobatie", value = 50), - Node(type = Type.SKILLS, label = "Perception", value = 55), - Node(type = Type.SKILLS, label = "Recherche", value = 25), - Node(type = Type.SKILLS, label = "Empathie", value = 15), - Node(type = Type.SKILLS, label = "Persuasion", value = 20), - Node(type = Type.SKILLS, label = "Intimidation", value = 50), - Node(type = Type.SKILLS, label = "Baratin", value = 20), - Node(type = Type.SKILLS, label = "Marchandage", value = 10), - Node(type = Type.SKILLS, label = "Escamotage", value = 20), - Node(type = Type.SKILLS, label = "Premiers soins", value = 20), - ), - occupations = listOf( - Node(type = Type.OCCUPATIONS, label = "Survie", value = 80), - Node(type = Type.OCCUPATIONS, label = "Empathie (Animal)", value = 60), - Node(type = Type.OCCUPATIONS, label = "Pistage", value = 60), - Node(type = Type.OCCUPATIONS, label = "Discrétion", value = 60), - Node(type = Type.OCCUPATIONS, label = "Connaissance (Herboristerie)", value = 40), - Node(type = Type.OCCUPATIONS, label = "Artisanat (Onguent)", value = 60), - ), - magics = listOf( - Node(type = Type.MAGICS, label = "Métamorphose (Loup)", value = 100), - ), - ) - } -} - -@OptIn(ExperimentalLayoutApi::class) -@Composable -fun CharacterSheetPage( - modifier: Modifier, - scrollState: ScrollState = rememberScrollState(), - width: Dp = 320.dp, - characterSheet: CharacterSheetPageUio = CharacterSheetPageUio.Koryas, - onCharacteristic: (characteristic: CharacterSheetPageUio.Characteristic) -> Unit, - onSkill: (skill: CharacterSheetPageUio.Node) -> Unit, -) { - Column( - modifier = Modifier - .verticalScroll(state = scrollState) - .padding(all = 16.dp) - .then(other = modifier), - verticalArrangement = Arrangement.spacedBy(space = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - modifier = Modifier.padding(bottom = 16.dp), - style = MaterialTheme.typography.h4, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - text = characterSheet.name, - ) - FlowRow( - maxItemsInEachRow = 3, - horizontalArrangement = Arrangement.spacedBy( - space = 16.dp, - alignment = Alignment.CenterHorizontally, - ), - verticalArrangement = Arrangement.spacedBy(space = 16.dp), - ) { - characterSheet.characteristics.forEach { - Stat( - modifier = Modifier - .width(width = width / 3 - 32.dp) - .height(height = 112.dp), - characteristic = it, - onClick = { onCharacteristic(it) }, - ) - } - } - DecoratedBox( - modifier = Modifier - .width(width = width) - .padding(vertical = 8.dp), - ) { - Column { - Text( - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - text = "Charactéristiques dérivées" - ) - characterSheet.subCharacteristics.forEach { - Characteristics( - modifier = Modifier.fillMaxWidth(), - characteristic = it, - ) - } - } - } - DecoratedBox( - modifier = Modifier - .width(width = width) - .padding(vertical = 8.dp), - ) { - Column { - Text( - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - text = "Compétences", - ) - characterSheet.skills.forEach { - Skill( - modifier = Modifier.fillMaxWidth(), - label = it.label, - value = it.value, - onClick = { onSkill(it) }, - ) - } - } - } - DecoratedBox( - modifier = Modifier - .width(width = width) - .padding(vertical = 8.dp), - ) { - Column { - Text( - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - text = "Occupations" - ) - characterSheet.occupations.forEach { - Skill( - modifier = Modifier.fillMaxWidth(), - label = it.label, - value = it.value, - onClick = { onSkill(it) }, - ) - } - } - } - DecoratedBox( - modifier = Modifier - .width(width = width) - .padding(vertical = 8.dp), - ) { - Column { - Text( - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - text = "Compétences magiques" - ) - characterSheet.magics.forEach { - Skill( - modifier = Modifier.fillMaxWidth(), - label = it.label, - value = it.value, - onClick = { onSkill(it) }, - ) - } - } - } - } -} - -@Composable -private fun Stat( - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(all = 8.dp), - characteristic: CharacterSheetPageUio.Characteristic, - onClick: () -> Unit, -) { - DecoratedBox( - modifier = Modifier - .clickable(onClick = onClick) - .padding(paddingValues = paddingValues) - .then(other = modifier), - ) { - Text( - modifier = Modifier.align(alignment = Alignment.TopCenter), - style = MaterialTheme.typography.caption, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - text = characteristic.label, - ) - Text( - modifier = Modifier.align(alignment = Alignment.Center), - style = MaterialTheme.typography.h4, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - text = characteristic.value - ) - } -} - -@Composable -private fun Characteristics( - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp), - characteristic: CharacterSheetPageUio.Characteristic, -) { - Row( - modifier = Modifier - .padding(paddingValues = paddingValues) - .then(other = modifier), - horizontalArrangement = Arrangement.SpaceBetween, - ) { - Text( - modifier = Modifier.alignByBaseline(), - style = MaterialTheme.typography.body1, - text = characteristic.label - ) - Text( - modifier = Modifier.alignByBaseline(), - style = MaterialTheme.typography.body1, - fontWeight = FontWeight.Bold, - text = characteristic.value, - ) - } -} - -@Composable -private fun Skill( - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(horizontal = 8.dp), - label: String, - value: Any, - onClick: () -> Unit, -) { - Row( - modifier = Modifier - .clickable(onClick = onClick) - .padding(paddingValues = paddingValues) - .then(other = modifier), - horizontalArrangement = Arrangement.spacedBy(space = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = Modifier.weight(1f), - style = MaterialTheme.typography.body1, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - text = label - ) - Text( - style = MaterialTheme.typography.body1, - fontWeight = FontWeight.Bold, - text = "$value", - ) - Checkbox( - modifier = Modifier.size(size = 32.dp), - checked = false, - onCheckedChange = { } - ) - } -} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetViewModel.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetViewModel.kt deleted file mode 100644 index 0df2958..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/CharacterSheetViewModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.pixelized.desktop.lwa.screen.characterSheet - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel - -class CharacterSheetViewModel : ViewModel() { - - private val _sheet = mutableStateOf(null) - val sheet: State get() = _sheet - - fun showCharacterSheet() { - _sheet.value = CharacterSheetPageUio.Koryas - } - - fun hideCharacterSheet() { - _sheet.value = null - } -} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt deleted file mode 100644 index 46862e5..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetEditPage.kt +++ /dev/null @@ -1,166 +0,0 @@ -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.fillMaxSize -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.Surface -import androidx.compose.material.Text -import androidx.compose.material.TextButton -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.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox -import com.pixelized.desktop.lwa.navigation.LocalScreen -import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio -import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.Form -import kotlinx.coroutines.launch - -@Stable -data class CharacterSheetEditPageUio( - val id: String, - val name: FieldUio, - val skills: List, -) { - @Stable - data class SkillGroup( - val title: String, - val type: Type, - val editable: Boolean = false, - val fields: List, - ) { - @Stable - enum class Type { - CHARACTERISTICS, - SUB_CHARACTERISTICS, - SKILLS, - OCCUPATIONS, - MAGICS, - OTHER, - } - } -} - - -@Composable -fun CharacterSheetEditPage( - viewModel: CharacterSheetEditViewModel = viewModel { CharacterSheetEditViewModel() }, -) { - val screen = LocalScreen.current - val scope = rememberCoroutineScope() - - Surface( - modifier = Modifier.fillMaxSize(), - ) { - CharacterSheetEdit( - form = viewModel.characterSheet.value, - onSkill = viewModel::onSkill, - onCancel = { screen.popBackStack() }, - onSave = { - scope.launch { - viewModel.save() - screen.popBackStack() - } - }, - ) - } -} - -@Composable -fun CharacterSheetEdit( - form: CharacterSheetEditPageUio, - onSkill: (CharacterSheetEditPageUio.SkillGroup) -> Unit, - onCancel: () -> Unit, - onSave: () -> 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.skills.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, - ) - } - } - } - } - } - } - } - - Row { - TextButton( - onClick = onCancel, - ) { - Text(text = "Annuler") - } - TextButton( - onClick = onSave, - ) { - Text(text = "Sauvegarder") - } - } - } -} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt deleted file mode 100644 index 6e44575..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/characterSheet/edit/CharacterSheetFactory.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.pixelized.desktop.lwa.screen.characterSheet.edit - -import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet -import com.pixelized.desktop.lwa.screen.characterSheet.edit.composable.FieldUio - -class CharacterSheetFactory { - - fun convertToModel(sheet: CharacterSheetEditPageUio): CharacterSheet { - return CharacterSheet( - id = sheet.id, - name = sheet.name.value.value, - strength = sheet.skills[0].fields[0].unpack(), - dexterity = sheet.skills[0].fields[1].unpack(), - constitution = sheet.skills[0].fields[2].unpack(), - height = sheet.skills[0].fields[3].unpack(), - intelligence = sheet.skills[0].fields[4].unpack(), - power = sheet.skills[0].fields[5].unpack(), - charisma = sheet.skills[0].fields[6].unpack(), - movement = sheet.skills[1].fields[0].unpack(), - currentHp = sheet.skills[1].fields[1].unpack(), - maxHp = sheet.skills[1].fields[1].unpack(), - currentPP = sheet.skills[1].fields[2].unpack(), - maxPP = sheet.skills[1].fields[2].unpack(), - damageBonus = sheet.skills[1].fields[3].unpack(), - armor = sheet.skills[1].fields[4].unpack(), - skills = sheet.skills[2].fields.map { - CharacterSheet.Skill( - label = it.label.value, - value = it.value.value.toIntOrNull() ?: 0, - used = false, - ) - }, - occupations = sheet.skills[3].fields.map { - CharacterSheet.Skill( - label = it.label.value, - value = it.value.value.toIntOrNull() ?: 0, - used = false, - ) - }, - magics = sheet.skills[4].fields.map { - CharacterSheet.Skill( - label = it.label.value, - value = it.value.value.toIntOrNull() ?: 0, - used = false, - ) - }, - attacks = emptyList(), - ) - } - - private inline fun FieldUio.unpack(): T { - val tmp = value.value.ifBlank { valuePlaceHolder.value } - return when (T::class) { - Int::class -> (tmp.toIntOrNull() ?: 0) as T - else -> tmp as T - } - } -} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt b/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt deleted file mode 100644 index 33efb6e..0000000 --- a/composeApp/src/desktopMain/kotlin/com/pixelized/desktop/lwa/screen/main/MainPage.kt +++ /dev/null @@ -1,108 +0,0 @@ -package com.pixelized.desktop.lwa.screen.main - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.TextButton -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.Edit -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.State -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.lifecycle.viewmodel.compose.viewModel -import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox -import com.pixelized.desktop.lwa.navigation.LocalScreen -import com.pixelized.desktop.lwa.navigation.destination.navigateToCharacterSheetEdit - -@Stable -data class CharacterUio( - val id: String, - val name: String, -) - -@Composable -fun MainPage( - viewModel: MainPageViewModel = viewModel { MainPageViewModel() }, -) { - val screen = LocalScreen.current - - Surface { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, - ) { - MainPageContent( - characters = viewModel.characters, - onCharacter = { - - }, - onCharacterEdit = { - - }, - onCharacterDelete = { - - }, - onCreateCharacter = { - screen.navigateToCharacterSheetEdit() - }, - ) - } - } -} - -@Composable -fun MainPageContent( - modifier: Modifier = Modifier, - characters: State>, - onCharacter: (CharacterUio) -> Unit, - onCharacterEdit: (CharacterUio) -> Unit, - onCharacterDelete: (CharacterUio) -> Unit, - onCreateCharacter: () -> Unit, -) { - Column( - modifier = modifier, - ) { - DecoratedBox { - Column { - characters.value.forEach { sheet -> - Row { - TextButton( - onClick = { onCharacter(sheet) }, - ) { - Text(text = sheet.name) - } - IconButton( - onClick = { onCharacterEdit(sheet) }, - ) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = null, - ) - } - IconButton( - onClick = { onCharacterDelete(sheet) }, - ) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = null, - ) - } - } - } - TextButton( - onClick = { onCreateCharacter() }, - ) { - Text(text = "Créer une feuille de personnage") - } - } - } - } -} \ No newline at end of file