Change the UI of damage & fatigue dialogs.
This commit is contained in:
parent
741bb7cf25
commit
894d8db493
15 changed files with 354 additions and 223 deletions
|
|
@ -84,6 +84,7 @@
|
|||
<string name="character_sheet_edit__sub_characteristics__max_hit_point">Points de vie maximum</string>
|
||||
<string name="character_sheet_edit__sub_characteristics__hit_point">Points de vie</string>
|
||||
<string name="character_sheet_edit__sub_characteristics__max_power_point">Points de pouvoir maximum</string>
|
||||
<string name="character_sheet_edit__use_armor">Utiliser l'armure</string>
|
||||
<string name="character_sheet_edit__sub_characteristics__power_point">Points de pouvoir</string>
|
||||
<string name="character_sheet_edit__sub_characteristics__damage_bonus">Bonus aux dégats</string>
|
||||
<string name="character_sheet_edit__sub_characteristics__armor">Armure</string>
|
||||
|
|
@ -253,6 +254,7 @@
|
|||
|
||||
<string name="chat__diminished_change">%1$s passe à %2$d d'état diminuée</string>
|
||||
<string name="chat__characteristic_change__hp_down">%1$s subit %2$d point(s) de dommage</string>
|
||||
<string name="chat__characteristic_change__hp_down_armor">%1$s subit %2$d point(s) de dommage (armure : %3$d)</string>
|
||||
<string name="chat__characteristic_change__hp_up">%1$s récupère %2$d point(s) de vie</string>
|
||||
<string name="chat__characteristic_change__pp_down">%1$s utilise %2$d point(s) de pouvoir</string>
|
||||
<string name="chat__characteristic_change__pp_up">%1$s récupère %2$d point(s) de pouvoir</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package com.pixelized.desktop.lwa.ui.composable.button
|
||||
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_remove_24dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
@Composable
|
||||
fun SignButton(
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
enabled: Boolean = true,
|
||||
add: StateFlow<Boolean>,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
val rotation = animateFloatAsState(
|
||||
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
|
||||
targetValue = when (add.collectAsState().value) {
|
||||
true -> 90f
|
||||
else -> 0f
|
||||
}
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
this.rotationZ = rotation.value * 2f
|
||||
},
|
||||
painter = painterResource(Res.drawable.ic_remove_24dp),
|
||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||
contentDescription = null
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
this.rotationZ = rotation.value * 3f
|
||||
},
|
||||
painter = painterResource(Res.drawable.ic_remove_24dp),
|
||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +1,43 @@
|
|||
package com.pixelized.desktop.lwa.ui.composable.character.characteristic
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
|
||||
import com.pixelized.desktop.lwa.ui.composable.button.SignButton
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.LwaDialog
|
||||
import com.pixelized.desktop.lwa.ui.composable.checkbox.LwaCheckBox
|
||||
import com.pixelized.desktop.lwa.ui.composable.checkbox.LwaCheckBoxUio
|
||||
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
|
||||
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
|
||||
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__use_armor
|
||||
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
|
||||
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -44,9 +47,10 @@ data class CharacterSheetCharacteristicDialogUio(
|
|||
val characterSheetId: String,
|
||||
val characteristic: Characteristic,
|
||||
val label: String,
|
||||
val value: () -> TextFieldValue,
|
||||
val onValueChange: (TextFieldValue) -> Unit,
|
||||
val maxValue: String,
|
||||
val add: StateFlow<Boolean>,
|
||||
val value: LwaTextFieldUio,
|
||||
val enableArmor: LwaCheckBoxUio?,
|
||||
val enableConfirm: StateFlow<Boolean>,
|
||||
) {
|
||||
@Stable
|
||||
enum class Characteristic {
|
||||
|
|
@ -55,125 +59,131 @@ data class CharacterSheetCharacteristicDialogUio(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CharacterSheetCharacteristicDialog(
|
||||
dialog: State<CharacterSheetCharacteristicDialogUio?>,
|
||||
onConfirm: (CharacterSheetCharacteristicDialogUio) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
dialog.value?.let {
|
||||
Dialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
content = {
|
||||
CharacterSheetCharacteristicContent(
|
||||
dialog = it,
|
||||
onConfirm = onConfirm,
|
||||
onDismissRequest = onDismissRequest,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
@Stable
|
||||
object CharacterSheetCharacteristicDialogDefault {
|
||||
@Stable
|
||||
val paddings: PaddingValues =
|
||||
PaddingValues(top = 16.dp, start = 8.dp, end = 8.dp, bottom = 8.dp)
|
||||
|
||||
@Stable
|
||||
val spacings: DpSize = DpSize(width = 4.dp, height = 8.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CharacterSheetCharacteristicContent(
|
||||
dialog: CharacterSheetCharacteristicDialogUio,
|
||||
fun CharacterSheetCharacteristicDialog(
|
||||
dialog: State<CharacterSheetCharacteristicDialogUio?>,
|
||||
paddings: PaddingValues = CharacterSheetCharacteristicDialogDefault.paddings,
|
||||
spacings: DpSize = CharacterSheetCharacteristicDialogDefault.spacings,
|
||||
onConfirm: (CharacterSheetCharacteristicDialogUio) -> Unit,
|
||||
onSwapSign: (CharacterSheetCharacteristicDialogUio) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val typography = MaterialTheme.typography
|
||||
val colors = MaterialTheme.colors
|
||||
LwaDialog(
|
||||
state = dialog,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onConfirm = { dialog.value?.let(onConfirm) }
|
||||
) { state ->
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
CharacterSheetCharacteristicDialogKeyHandler(
|
||||
onSwap = { onSwapSign(state) },
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = onDismissRequest,
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues = paddings),
|
||||
verticalArrangement = Arrangement.spacedBy(space = spacings.height),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = state.label,
|
||||
)
|
||||
.onPreviewEscape(
|
||||
escape = onDismissRequest,
|
||||
enter = { onConfirm(dialog) },
|
||||
)
|
||||
.fillMaxSize()
|
||||
.padding(all = 32.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
DecoratedBox {
|
||||
Surface {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(space = spacings.width),
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
SignButton(
|
||||
modifier = Modifier
|
||||
.size(size = 56.dp)
|
||||
.background(
|
||||
color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
|
||||
shape = MaterialTheme.shapes.small,
|
||||
),
|
||||
add = state.add,
|
||||
onClick = { onSwapSign(state) },
|
||||
)
|
||||
LwaTextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester = focusRequester)
|
||||
.width(192.dp),
|
||||
field = state.value,
|
||||
)
|
||||
}
|
||||
state.enableArmor?.let {
|
||||
Row (
|
||||
modifier = Modifier.align(alignment = Alignment.End),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = spacings.width,
|
||||
alignment = Alignment.End
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, start = 24.dp, end = 24.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = dialog.label,
|
||||
text = stringResource(Res.string.character_sheet_edit__use_armor),
|
||||
)
|
||||
LwaCheckBox(
|
||||
field = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.align(alignment = Alignment.End),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = spacings.width,
|
||||
alignment = Alignment.End
|
||||
)
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onDismissRequest,
|
||||
) {
|
||||
Text(
|
||||
color = MaterialTheme.colors.primary.copy(alpha = .7f),
|
||||
text = stringResource(Res.string.dialog__cancel_action)
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
enabled = state.enableConfirm.collectAsState().value,
|
||||
onClick = { onConfirm(state) },
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.dialog__confirm_action)
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
BasicTextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester = focusRequester)
|
||||
.alignByBaseline()
|
||||
.width(width = 120.dp),
|
||||
textStyle = remember {
|
||||
typography.h5.copy(
|
||||
color = colors.primary,
|
||||
textAlign = TextAlign.End,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
cursorBrush = SolidColor(MaterialTheme.colors.primary),
|
||||
singleLine = true,
|
||||
keyboardActions = KeyboardActions { onConfirm(dialog) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
value = dialog.value(),
|
||||
onValueChange = dialog.onValueChange,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = "/",
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = dialog.maxValue,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
.align(alignment = Alignment.End),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = 4.dp,
|
||||
alignment = Alignment.End
|
||||
)
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onDismissRequest,
|
||||
) {
|
||||
Text(
|
||||
color = MaterialTheme.colors.primary.copy(alpha = .7f),
|
||||
text = stringResource(Res.string.dialog__cancel_action)
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
onClick = { onConfirm(dialog) },
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.dialog__confirm_action)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun CharacterSheetCharacteristicDialogKeyHandler(
|
||||
onSwap: () -> Unit,
|
||||
) {
|
||||
KeyHandler {
|
||||
if (it.type == KeyEventType.KeyDown) {
|
||||
when (it.key) {
|
||||
Key.AltLeft, Key.AltRight -> {
|
||||
onSwap()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,22 @@
|
|||
package com.pixelized.desktop.lwa.ui.composable.character.characteristic
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio.Characteristic
|
||||
import com.pixelized.desktop.lwa.ui.composable.checkbox.createLwaCheckBox
|
||||
import com.pixelized.desktop.lwa.ui.composable.checkbox.createLwaCheckBoxFlow
|
||||
import com.pixelized.desktop.lwa.ui.composable.textfield.createLwaTextField
|
||||
import com.pixelized.desktop.lwa.ui.composable.textfield.createLwaTextFieldFlow
|
||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character__inventory__add_to_purse__title
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__hit_point
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sub_characteristics__power_point
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
|
@ -19,9 +26,13 @@ class CharacterSheetCharacteristicDialogFactory(
|
|||
private val alterationRepository: AlterationRepository,
|
||||
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
|
||||
) {
|
||||
private val decimalChecker = Regex("""^\s*\d*\s*${'$'}""")
|
||||
|
||||
suspend fun convertToDialogUio(
|
||||
scope: CoroutineScope,
|
||||
characterSheetId: String?,
|
||||
characteristic: Characteristic,
|
||||
signFlow: StateFlow<Boolean>,
|
||||
): CharacterSheetCharacteristicDialogUio? {
|
||||
|
||||
if (characterSheetId == null) return null
|
||||
|
|
@ -32,10 +43,10 @@ class CharacterSheetCharacteristicDialogFactory(
|
|||
|
||||
if (characterSheet == null) return null
|
||||
|
||||
val alterations: Map<String, List<FieldAlteration>> = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
|
||||
val alterations: Map<String, List<FieldAlteration>> =
|
||||
alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
|
||||
characterSheet = characterSheet,
|
||||
alterations = alterations,
|
||||
|
|
@ -43,36 +54,61 @@ class CharacterSheetCharacteristicDialogFactory(
|
|||
|
||||
return when (characteristic) {
|
||||
Characteristic.Damage -> {
|
||||
val value = mutableStateOf(
|
||||
"${alteredCharacterSheet.maxHp - alteredCharacterSheet.damage}".let {
|
||||
TextFieldValue(text = it, selection = TextRange(it.length))
|
||||
}
|
||||
val valueFlow = createLwaTextFieldFlow(
|
||||
label = null,
|
||||
)
|
||||
val armor = if (alteredCharacterSheet.armor > 0) {
|
||||
createLwaCheckBoxFlow(
|
||||
checked = true,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
CharacterSheetCharacteristicDialogUio(
|
||||
characterSheetId = characterSheetId,
|
||||
characteristic = characteristic,
|
||||
label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__hit_point),
|
||||
value = { value.value },
|
||||
onValueChange = { value.value = it },
|
||||
maxValue = "${alteredCharacterSheet.maxHp}",
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__hit_point).let {
|
||||
"$it : ${alteredCharacterSheet.maxHp - alteredCharacterSheet.damage}/${alteredCharacterSheet.maxHp}"
|
||||
},
|
||||
add = signFlow,
|
||||
value = valueFlow.createLwaTextField {
|
||||
valueFlow.errorFlow.value = check(it)
|
||||
valueFlow.valueFlow.value = it
|
||||
},
|
||||
enableArmor = armor?.createLwaCheckBox(),
|
||||
enableConfirm = valueFlow.errorFlow.map { it.not() }.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Lazily,
|
||||
initialValue = valueFlow.errorFlow.value.not(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Characteristic.Fatigue -> {
|
||||
val value = mutableStateOf(
|
||||
"${alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue}".let {
|
||||
TextFieldValue(text = it, selection = TextRange(it.length))
|
||||
}
|
||||
val valueFlow = createLwaTextFieldFlow(
|
||||
label = null,
|
||||
)
|
||||
CharacterSheetCharacteristicDialogUio(
|
||||
characterSheetId = characterSheetId,
|
||||
characteristic = characteristic,
|
||||
label = getString(resource = Res.string.character_sheet_edit__sub_characteristics__power_point),
|
||||
value = { value.value },
|
||||
onValueChange = { value.value = it },
|
||||
maxValue = "${alteredCharacterSheet.maxPp}",
|
||||
label = getString(Res.string.character_sheet_edit__sub_characteristics__power_point).let {
|
||||
"$it : ${alteredCharacterSheet.maxPp - alteredCharacterSheet.fatigue}/${alteredCharacterSheet.maxPp}"
|
||||
},
|
||||
add = signFlow,
|
||||
value = valueFlow.createLwaTextField {
|
||||
valueFlow.errorFlow.value = check(it)
|
||||
valueFlow.valueFlow.value = it
|
||||
},
|
||||
enableArmor = null,
|
||||
enableConfirm = valueFlow.errorFlow.map { it.not() }.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Lazily,
|
||||
initialValue = valueFlow.errorFlow.value.not(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun check(value: String): Boolean = !decimalChecker.matches(value)
|
||||
}
|
||||
|
|
@ -3,12 +3,15 @@ package com.pixelized.desktop.lwa.ui.composable.character.characteristic
|
|||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio.Characteristic
|
||||
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
|
||||
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class CharacterSheetCharacteristicDialogViewModel(
|
||||
private val characterSheetRepository: CharacterSheetRepository,
|
||||
|
|
@ -18,6 +21,8 @@ class CharacterSheetCharacteristicDialogViewModel(
|
|||
private val network: NetworkRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private val signFlow = MutableStateFlow(false)
|
||||
|
||||
private val _statChangeDialog = mutableStateOf<CharacterSheetCharacteristicDialogUio?>(null)
|
||||
val statChangeDialog: State<CharacterSheetCharacteristicDialogUio?> get() = _statChangeDialog
|
||||
|
||||
|
|
@ -25,19 +30,27 @@ class CharacterSheetCharacteristicDialogViewModel(
|
|||
_statChangeDialog.value = null
|
||||
}
|
||||
|
||||
fun swapCharacteristicSign() {
|
||||
signFlow.update { it.not() }
|
||||
}
|
||||
|
||||
suspend fun showSubCharacteristicDialog(
|
||||
characterSheetId: String?,
|
||||
characteristic: Characteristic,
|
||||
) {
|
||||
signFlow.update { false }
|
||||
_statChangeDialog.value = factory.convertToDialogUio(
|
||||
scope = viewModelScope,
|
||||
characterSheetId = characterSheetId,
|
||||
characteristic = characteristic,
|
||||
signFlow = signFlow,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun changeSubCharacteristic(
|
||||
characterSheetId: String?,
|
||||
characteristic: Characteristic,
|
||||
useArmor: Boolean,
|
||||
value: Int,
|
||||
) {
|
||||
if (characterSheetId == null) return
|
||||
|
|
@ -49,29 +62,37 @@ class CharacterSheetCharacteristicDialogViewModel(
|
|||
|
||||
if (characterSheet == null) return
|
||||
|
||||
val alterations = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
|
||||
// we need the maximum HP / Power that the character sheet have.
|
||||
val alteredSheet = alteredCharacterSheetFactory.sheet(
|
||||
characterSheet = characterSheet,
|
||||
alterations = alterations,
|
||||
)
|
||||
val sign = if (signFlow.value) 1 else -1
|
||||
|
||||
val message = when (characteristic) {
|
||||
Characteristic.Damage -> CharacterSheetEvent.UpdateDamage(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
characterSheetId = characterSheetId,
|
||||
oldValue = alteredSheet.damage,
|
||||
damage = alteredSheet.maxHp - value,
|
||||
)
|
||||
Characteristic.Damage -> {
|
||||
val armor = if (useArmor && signFlow.value.not()) {
|
||||
val alterations = alterationRepository.activeFieldAlterations(
|
||||
characterSheetId = characterSheetId,
|
||||
)
|
||||
val alteredSheet = alteredCharacterSheetFactory.sheet(
|
||||
characterSheet = characterSheet,
|
||||
alterations = alterations,
|
||||
)
|
||||
alteredSheet.armor
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
CharacterSheetEvent.UpdateDamage(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
characterSheetId = characterSheetId,
|
||||
oldValue = characterSheet.damage,
|
||||
damage = characterSheet.damage - value * sign - (armor ?: 0),
|
||||
armor = armor,
|
||||
)
|
||||
}
|
||||
|
||||
Characteristic.Fatigue -> CharacterSheetEvent.UpdateFatigue(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
characterSheetId = characterSheetId,
|
||||
oldValue = alteredSheet.fatigue,
|
||||
fatigue = alteredSheet.maxPp - value,
|
||||
oldValue = characterSheet.fatigue,
|
||||
fatigue = characterSheet.fatigue - value * sign,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package com.pixelized.desktop.lwa.ui.composable.character.purse
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
|
|
@ -19,8 +16,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -35,13 +30,13 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.desktop.lwa.ui.composable.button.SignButton
|
||||
import com.pixelized.desktop.lwa.ui.composable.character.LwaDialog
|
||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
|
||||
|
|
@ -53,8 +48,6 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
|
||||
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_remove_24dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Stable
|
||||
|
|
@ -94,6 +87,10 @@ fun PurseDialog(
|
|||
val focusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
|
||||
PurseDialogKeyHandler(
|
||||
onSwap = { onSwapSign(state) },
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues = paddings),
|
||||
verticalArrangement = Arrangement.spacedBy(space = spacings.height),
|
||||
|
|
@ -162,10 +159,6 @@ fun PurseDialog(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
PurseDialogKeyHandler(
|
||||
onSwap = { onSwapSign(state) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,46 +195,6 @@ private fun PurseContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SignButton(
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
enabled: Boolean = true,
|
||||
add: StateFlow<Boolean>,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
val rotation = animateFloatAsState(
|
||||
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
|
||||
targetValue = when (add.collectAsState().value) {
|
||||
true -> 90f
|
||||
else -> 0f
|
||||
}
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
this.rotationZ = rotation.value * 2f
|
||||
},
|
||||
painter = painterResource(Res.drawable.ic_remove_24dp),
|
||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||
contentDescription = null
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
this.rotationZ = rotation.value * 3f
|
||||
},
|
||||
painter = painterResource(Res.drawable.ic_remove_24dp),
|
||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PurseDialogKeyHandler(
|
||||
onSwap: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.pixelized.desktop.lwa.ui.composable.checkbox
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@Stable
|
||||
data class LwaCheckBoxFlow(
|
||||
val checkedFlow: MutableStateFlow<Boolean>,
|
||||
)
|
||||
|
||||
@Stable
|
||||
fun createLwaCheckBoxFlow(
|
||||
checked: Boolean,
|
||||
): LwaCheckBoxFlow {
|
||||
return LwaCheckBoxFlow(
|
||||
checkedFlow = MutableStateFlow(checked),
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
fun LwaCheckBoxFlow.createLwaCheckBox(
|
||||
enable: Boolean = true,
|
||||
onCheckedChange: (Boolean) -> Unit = { checkedFlow.value = it },
|
||||
): LwaCheckBoxUio {
|
||||
return LwaCheckBoxUio(
|
||||
enable = enable,
|
||||
checked = checkedFlow,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
|
|
@ -6,13 +6,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
@Stable
|
||||
data class LwaTextFieldFlow(
|
||||
val errorFlow: MutableStateFlow<Boolean>,
|
||||
val valueFlow: MutableStateFlow<String>,
|
||||
val labelFlow: MutableStateFlow<String?>,
|
||||
val valueFlow: MutableStateFlow<String>,
|
||||
)
|
||||
|
||||
fun createLwaTextFieldFlow(
|
||||
error: Boolean = false,
|
||||
label: String,
|
||||
label: String?,
|
||||
value: String = "",
|
||||
): LwaTextFieldFlow {
|
||||
return createLwaTextFieldFlow(
|
||||
|
|
@ -24,8 +24,8 @@ fun createLwaTextFieldFlow(
|
|||
|
||||
fun createLwaTextFieldFlow(
|
||||
errorFlow: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
valueFlow: MutableStateFlow<String>,
|
||||
labelFlow: MutableStateFlow<String?>,
|
||||
valueFlow: MutableStateFlow<String>,
|
||||
): LwaTextFieldFlow {
|
||||
return LwaTextFieldFlow(
|
||||
errorFlow = errorFlow,
|
||||
|
|
|
|||
|
|
@ -206,12 +206,16 @@ fun CampaignScreen(
|
|||
characteristicDialogViewModel.changeSubCharacteristic(
|
||||
characterSheetId = dialog.characterSheetId,
|
||||
characteristic = dialog.characteristic,
|
||||
value = dialog.value().text.toIntOrNull() ?: 0,
|
||||
useArmor= dialog.enableArmor?.checked?.value == true,
|
||||
value = dialog.value.valueFlow.value.toIntOrNull() ?: 0,
|
||||
)
|
||||
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
||||
blurController.hide()
|
||||
}
|
||||
},
|
||||
onSwapSign = {
|
||||
characteristicDialogViewModel.swapCharacteristicSign()
|
||||
},
|
||||
onDismissRequest = {
|
||||
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
||||
blurController.hide()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.pixelized.shared.lwa.protocol.websocket.RollEvent
|
|||
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down
|
||||
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down_armor
|
||||
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_up
|
||||
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__pp_down
|
||||
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__pp_up
|
||||
|
|
@ -94,16 +95,19 @@ class TextMessageFactory(
|
|||
?: return null
|
||||
// We are talking about damage / consumption there so old value have to be put first.
|
||||
val value = message.oldValue - message.damage
|
||||
val armor = message.armor
|
||||
|
||||
CharacteristicTextMessageUio(
|
||||
id = "${message.timestamp}-${message.characterSheetId}-Damage",
|
||||
timestamp = formatTime.format(message.timestamp),
|
||||
label = when {
|
||||
value < 0 -> Res.string.chat__characteristic_change__hp_down
|
||||
else -> Res.string.chat__characteristic_change__hp_up
|
||||
value >= 0 -> Res.string.chat__characteristic_change__hp_up
|
||||
armor != null -> Res.string.chat__characteristic_change__hp_down_armor
|
||||
else -> Res.string.chat__characteristic_change__hp_down
|
||||
},
|
||||
character = sheet.name,
|
||||
value = abs(value),
|
||||
armor = message.armor,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -127,6 +131,7 @@ class TextMessageFactory(
|
|||
},
|
||||
character = sheet.name,
|
||||
value = abs(value),
|
||||
armor = 0,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ data class CharacteristicTextMessageUio(
|
|||
val label: StringResource,
|
||||
val character: String,
|
||||
val value: Int,
|
||||
val armor: Int?,
|
||||
) : TextMessage
|
||||
|
||||
@Composable
|
||||
|
|
@ -52,7 +53,7 @@ fun CharacteristicTextMessage(
|
|||
style = MaterialTheme.lwa.typography.chat.text,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = stringResource(message.label, message.character, message.value),
|
||||
text = stringResource(message.label, message.character, message.value, message.armor ?: 0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -126,12 +126,16 @@ fun GMCharacterPage(
|
|||
characteristicDialogViewModel.changeSubCharacteristic(
|
||||
characterSheetId = dialog.characterSheetId,
|
||||
characteristic = dialog.characteristic,
|
||||
value = dialog.value().text.toIntOrNull() ?: 0,
|
||||
useArmor= dialog.enableArmor?.checked?.value == true,
|
||||
value = dialog.value.valueFlow.value.toIntOrNull() ?: 0,
|
||||
)
|
||||
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
||||
blurController.hide()
|
||||
}
|
||||
},
|
||||
onSwapSign = {
|
||||
characteristicDialogViewModel.swapCharacteristicSign()
|
||||
},
|
||||
onDismissRequest = {
|
||||
characteristicDialogViewModel.hideSubCharacteristicDialog()
|
||||
blurController.hide()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ fun Engine.putCharacterDamage(): suspend RoutingContext.() -> Unit {
|
|||
name = "damage",
|
||||
code = APIResponse.ErrorCode.Damage,
|
||||
)
|
||||
val armor: Int = call.queryParameters.param(
|
||||
name = "armor",
|
||||
code = APIResponse.ErrorCode.Armor,
|
||||
)
|
||||
// fetch the character sheet
|
||||
val characterSheet = characterService.character(characterSheetId)
|
||||
?: error("CharacterSheet with id:$characterSheetId not found.")
|
||||
|
|
@ -36,6 +40,7 @@ fun Engine.putCharacterDamage(): suspend RoutingContext.() -> Unit {
|
|||
characterSheetId = characterSheetId,
|
||||
oldValue = characterSheet.damage,
|
||||
damage = damage,
|
||||
armor = armor,
|
||||
)
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ data class APIResponse<T>(
|
|||
Active,
|
||||
Equip,
|
||||
Damage,
|
||||
Armor,
|
||||
Fatigue,
|
||||
Diminished,
|
||||
Count,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ sealed interface CharacterSheetEvent : SocketMessage, CharacterSheetIdMessage {
|
|||
data class UpdateDamage(
|
||||
override val characterSheetId: String,
|
||||
override val timestamp: Long,
|
||||
val armor: Int?,
|
||||
val oldValue: Int,
|
||||
val damage: Int,
|
||||
) : CharacterSheetEvent
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue