Link the CharacterSheet model to the UI.
This commit is contained in:
parent
65aa53890f
commit
b71645a7a2
30 changed files with 1113 additions and 961 deletions
Binary file not shown.
|
|
@ -0,0 +1,21 @@
|
|||
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.theme.LwaTheme
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun App() {
|
||||
LwaTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
MainNavHost()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import androidx.navigation.compose.NavHost
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import com.pixelized.desktop.lwa.navigation.destination.MainDestination
|
||||
import com.pixelized.desktop.lwa.navigation.destination.composableCharacterSheetEditPage
|
||||
import com.pixelized.desktop.lwa.navigation.destination.composableCharacterSheetPage
|
||||
import com.pixelized.desktop.lwa.navigation.destination.composableMainPage
|
||||
|
||||
val LocalScreen = compositionLocalOf<NavHostController> {
|
||||
|
|
@ -28,6 +29,7 @@ fun MainNavHost(
|
|||
) {
|
||||
composableMainPage()
|
||||
|
||||
composableCharacterSheetPage()
|
||||
composableCharacterSheetEditPage()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String>(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)
|
||||
}
|
||||
|
|
@ -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<String>(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)
|
||||
}
|
||||
|
|
@ -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<List<CharacterSheet>> {
|
||||
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<List<CharacterSheet>> {
|
||||
return sheets
|
||||
}
|
||||
|
||||
fun characterSheet(id: String): StateFlow<CharacterSheet?> {
|
||||
return preferences.loadFlow()
|
||||
fun characterSheetFlow(id: String?): StateFlow<CharacterSheet?> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Characteristic>,
|
||||
val subCharacteristics: List<Characteristic>,
|
||||
val skills: List<Node>,
|
||||
val occupations: List<Node>,
|
||||
val magics: List<Node>,
|
||||
) {
|
||||
@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 = { })
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CharacterSheetPageUio?>
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<SkillGroup>,
|
||||
) {
|
||||
@Stable
|
||||
data class SkillGroup(
|
||||
val title: String,
|
||||
val type: Type,
|
||||
val editable: Boolean = false,
|
||||
val fields: List<FieldUio>,
|
||||
) {
|
||||
@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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -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<CharacterSheetEditPageUio> 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<FieldUio>().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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CharacterSheetEditPageUio>
|
||||
val characterSheet: State<CharacterSheetEditPageUio> 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<FieldUio>().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 <reified T> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<CharacterUio>>,
|
||||
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",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,9 @@ class MainPageViewModel : ViewModel() {
|
|||
val characters: State<List<CharacterUio>>
|
||||
@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() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.pixelized.desktop.lwa.utils.extention
|
||||
|
||||
val String.ARG: String get() = "$this={$this}"
|
||||
|
|
@ -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<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 = { 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<FieldUio>().apply {
|
||||
// addAll(group.fields)
|
||||
// add(
|
||||
// FieldUio.create(
|
||||
// label = "",
|
||||
// valuePlaceHolder = { "40" },
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
// } else {
|
||||
// group
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<Characteristic>,
|
||||
val subCharacteristics: List<Characteristic>,
|
||||
val skills: List<Node>,
|
||||
val occupations: List<Node>,
|
||||
val magics: List<Node>,
|
||||
) {
|
||||
@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 = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CharacterSheetPageUio?>(null)
|
||||
val sheet: State<CharacterSheetPageUio?> get() = _sheet
|
||||
|
||||
fun showCharacterSheet() {
|
||||
_sheet.value = CharacterSheetPageUio.Koryas
|
||||
}
|
||||
|
||||
fun hideCharacterSheet() {
|
||||
_sheet.value = 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<SkillGroup>,
|
||||
) {
|
||||
@Stable
|
||||
data class SkillGroup(
|
||||
val title: String,
|
||||
val type: Type,
|
||||
val editable: Boolean = false,
|
||||
val fields: List<FieldUio>,
|
||||
) {
|
||||
@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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <reified T> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<CharacterUio>>,
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue