Add dynamic alteration inapp management.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2025-04-03 13:41:45 +02:00
parent 2f4b30297c
commit 2f6d0b3819
34 changed files with 639 additions and 97 deletions

View file

@ -15,10 +15,12 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsStore
import com.pixelized.desktop.lwa.repository.tag.TagRepository
import com.pixelized.desktop.lwa.repository.tag.TagStore
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogFactory
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogFactory
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogFactory
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlayViewModel
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.network.NetworkFactory
@ -128,6 +130,7 @@ val factoryDependencies
factoryOf(::CharacterDetailFactory)
factoryOf(::CharacterSheetCharacteristicDialogFactory)
factoryOf(::CharacterSheetDiminishedDialogFactory)
factoryOf(::CharacterSheetAlterationDialogFactory)
factoryOf(::TextMessageFactory)
factoryOf(::LevelUpFactory)
factoryOf(::GMTagFactory)
@ -148,8 +151,9 @@ val viewModelDependencies
viewModelOf(::PlayerRibbonViewModel)
viewModelOf(::NpcRibbonViewModel)
viewModelOf(::CharacterDetailViewModel)
viewModelOf(::CharacterSheetDiminishedViewModel)
viewModelOf(::CharacterDetailCharacteristicDialogViewModel)
viewModelOf(::CharacterSheetDiminishedDialogViewModel)
viewModelOf(::CharacterSheetCharacteristicDialogViewModel)
viewModelOf(::CharacterSheetAlterationDialogViewModel)
viewModelOf(::CampaignChatViewModel)
viewModelOf(::SettingsViewModel)
viewModelOf(::LevelUpViewModel)

View file

@ -79,7 +79,7 @@ interface LwaClient {
suspend fun putCharacterAlteration(
characterSheetId: String,
alterationId: Int,
alterationId: String,
active: Boolean,
): APIResponse<Unit>

View file

@ -142,7 +142,7 @@ class LwaClientImpl(
@Throws
override suspend fun putCharacterAlteration(
characterSheetId: String,
alterationId: Int,
alterationId: String,
active: Boolean,
) = client
.put("$root/character/update/alteration?characterSheetId=$characterSheetId&alterationId=$alterationId&active=$active")

View file

@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class CharacterSheetRepository(
private val store: CharacterSheetStore,
@ -64,6 +63,19 @@ class CharacterSheetRepository(
store.deleteCharacterSheet(characterSheetId = characterSheetId)
}
@Throws
suspend fun updateAlteration(
characterSheetId: String,
alterationId: String,
active: Boolean,
) {
store.putCharacterAlteration(
characterSheetId = characterSheetId,
alterationId = alterationId,
active = active,
)
}
fun checkCharacterSheetIdValidity(
characterSheetId: String,
): Boolean {

View file

@ -94,7 +94,7 @@ class CharacterSheetStore(
) {
val json = factory.convertToJson(sheet = sheet)
val request = client.putCharacter(sheet = json)
if (request.success) {
if (request.success.not()) {
LwaClient.error(error = request)
}
}
@ -104,7 +104,23 @@ class CharacterSheetStore(
characterSheetId: String,
) {
val request = client.deleteCharacterSheet(characterSheetId = characterSheetId)
if (request.success) {
if (request.success.not()) {
LwaClient.error(error = request)
}
}
@Throws
suspend fun putCharacterAlteration(
characterSheetId: String,
alterationId: String,
active: Boolean,
) {
val request = client.putCharacterAlteration(
characterSheetId = characterSheetId,
alterationId = alterationId,
active = active,
)
if (request.success.not()) {
LwaClient.error(error = request)
}
}

View file

@ -0,0 +1,84 @@
package com.pixelized.desktop.lwa.ui.composable.character.alterteration
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTag
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
import com.pixelized.desktop.lwa.ui.theme.lwa
@Stable
data class AlterationToggleItemUio(
val id: String,
val label: String,
val tags: List<GMTagUio>,
val active: Boolean,
)
@Stable
object AlterationToggleItemDefault {
val padding = PaddingValues(start = 8.dp)
}
@Composable
fun AlterationToggleItem(
modifier: Modifier = Modifier,
alteration: AlterationToggleItemUio,
padding: PaddingValues = AlterationToggleItemDefault.padding,
onAlteration: () -> Unit,
onTag: (String) -> Unit,
) {
Row(
modifier = Modifier
.clip(shape = MaterialTheme.lwa.shapes.gameMaster)
.clickable(onClick = onAlteration)
.background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp)
.minimumInteractiveComponentSize()
.padding(paddingValues = padding)
.then(other = modifier),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
val animatedColor = animateColorAsState(
targetValue = when (alteration.active) {
true -> MaterialTheme.lwa.colorScheme.base.secondary
else -> MaterialTheme.lwa.colorScheme.base.onSurface
}
)
Text(
style = MaterialTheme.lwa.typography.base.body1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
color = animatedColor.value,
text = alteration.label,
)
Row(
modifier = Modifier.weight(1f).height(intrinsicSize = IntrinsicSize.Min),
horizontalArrangement = Arrangement.spacedBy(space = 2.dp, alignment = Alignment.End)
) {
alteration.tags.forEach { tag ->
GMTag(
elevation = 4.dp,
tag = tag,
onTag = { onTag(tag.id) },
)
}
}
}
}

View file

@ -0,0 +1,154 @@
package com.pixelized.desktop.lwa.ui.composable.character.alterteration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
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
import androidx.compose.material.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMFilterHeader
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
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_close_24dp
import org.jetbrains.compose.resources.painterResource
@Stable
data class CharacterSheetAlterationDialogUio(
val characterSheetId: String,
val characterName: String,
val filter: LwaTextFieldUio,
val tags: StateFlow<List<GMTagUio>>,
val alterations: List<AlterationToggleItemUio>,
)
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun CharacterSheetAlterationDialog(
dialog: State<CharacterSheetAlterationDialogUio?>,
onConfirm: (CharacterSheetAlterationDialogUio) -> Unit,
onTag: (String) -> Unit,
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = DialogProperties(
usePlatformDefaultWidth = false,
usePlatformInsets = false,
),
onDismissRequest = onDismissRequest,
content = {
CharacterSheetAlterationContent(
dialog = it,
onConfirm = onConfirm,
onTag = onTag,
onAlteration = onAlteration,
onDismissRequest = onDismissRequest,
)
}
)
}
}
@Composable
fun CharacterSheetAlterationContent(
dialog: CharacterSheetAlterationDialogUio,
onConfirm: (CharacterSheetAlterationDialogUio) -> Unit,
onTag: (String) -> Unit,
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
onDismissRequest: () -> Unit,
) {
Surface(
modifier = Modifier
.fillMaxHeight()
.width(width = 128.dp * 4)
.padding(vertical = 16.dp),
) {
Column(
modifier = Modifier.fillMaxSize(),
) {
Row(
modifier = Modifier.padding(start = 16.dp).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface,
text = dialog.characterName,
)
IconButton(
onClick = onDismissRequest,
) {
Icon(
painter = painterResource(Res.drawable.ic_close_24dp),
tint = MaterialTheme.lwa.colorScheme.base.primary,
contentDescription = null,
)
}
}
GMFilterHeader(
filter = dialog.filter,
tags = dialog.tags.collectAsState(),
onTag = onTag,
)
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(weight = 1f),
contentPadding = PaddingValues(start = 8.dp, end = 8.dp, bottom = 8.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
items(
items = dialog.alterations,
key = { it.id }
) { alteration ->
AlterationToggleItem(
modifier = Modifier
.animateItem()
.background(
color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
shape = MaterialTheme.lwa.shapes.base.small,
)
.minimumInteractiveComponentSize()
.padding(horizontal = 8.dp),
alteration = alteration,
onAlteration = {
onAlteration(
dialog.characterSheetId,
alteration.id,
alteration.active,
)
},
onTag = onTag,
)
}
}
}
}
}

View file

@ -0,0 +1,61 @@
package com.pixelized.desktop.lwa.ui.composable.character.alterteration
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagFactory
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
import com.pixelized.desktop.lwa.utils.extention.unAccent
import com.pixelized.shared.lwa.model.alteration.Alteration
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.model.tag.Tag
import kotlinx.coroutines.flow.StateFlow
class CharacterSheetAlterationDialogFactory(
private val tagFactory: GMTagFactory,
) {
fun filterAlteration(
alterations: Collection<Alteration>,
unAccentFilter: String,
selectedTagId: String?,
): List<Alteration> {
return alterations.filter {
val matchName = it.metadata.name.unAccent().contains(
other = unAccentFilter,
ignoreCase = true
)
val matchTag = selectedTagId == null || it.tags.contains(
element = selectedTagId
)
matchName && matchTag
}
}
fun convertToDialogUio(
characterSheet: CharacterSheet?,
alterations: List<Alteration>,
tagMap: Map<String, Tag>,
filter: LwaTextFieldUio,
tags: StateFlow<List<GMTagUio>>,
selectedTagId: String?,
): CharacterSheetAlterationDialogUio? {
if (characterSheet == null) return null
return CharacterSheetAlterationDialogUio(
characterSheetId = characterSheet.id,
characterName = characterSheet.name,
filter = filter,
tags = tags,
alterations = alterations.map { alteration ->
AlterationToggleItemUio(
id = alteration.id,
label = alteration.metadata.name,
tags = tagFactory.convertToGMTagItemUio(
tags = alteration.tags.mapNotNull { tagMap[it] },
selectedTagId = selectedTagId
),
active = characterSheet.alterations.contains(alteration.id),
)
},
)
}
}

View file

@ -0,0 +1,121 @@
package com.pixelized.desktop.lwa.ui.composable.character.alterteration
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.tag.TagRepository
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagFactory
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
import com.pixelized.desktop.lwa.utils.extention.unAccent
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.game_master__alteration__filter
import org.jetbrains.compose.resources.getString
class CharacterSheetAlterationDialogViewModel(
private val characterSheetRepository: CharacterSheetRepository,
alterationRepository: AlterationRepository,
tagRepository: TagRepository,
dialogFactory: CharacterSheetAlterationDialogFactory,
tagFactory: GMTagFactory,
) : ViewModel() {
private val selectedCharacterSheetIdFlow = MutableStateFlow<String?>(null)
private val selectedAlterationNameFlow = MutableStateFlow("")
private val selectedTagIdFlow = MutableStateFlow<String?>(null)
private val selectedAlterationsFlow = combine(
alterationRepository.alterationFlow.map { it.values },
selectedAlterationNameFlow.map { it.unAccent() },
selectedTagIdFlow,
dialogFactory::filterAlteration
)
private val filter = LwaTextFieldUio(
enable = true,
isError = MutableStateFlow(false),
valueFlow = selectedAlterationNameFlow,
label = runBlocking { getString(Res.string.game_master__alteration__filter) },
placeHolder = null,
onValueChange = { selectedAlterationNameFlow.value = it },
)
private val tags: StateFlow<List<GMTagUio>> = combine(
tagRepository.alterationsTagFlow(),
selectedTagIdFlow,
) { tags, selectedTagId ->
tagFactory.convertToGMTagItemUio(
tags = tags.values,
selectedTagId = selectedTagId,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = emptyList(),
)
@OptIn(ExperimentalCoroutinesApi::class)
val alterationDialog = selectedCharacterSheetIdFlow
.flatMapLatest { characterSheetId ->
combine(
characterSheetRepository.characterDetailFlow(characterSheetId = characterSheetId),
tagRepository.alterationsTagFlow(),
selectedAlterationsFlow,
selectedTagIdFlow,
) { characterSheet, tagMap, alterations, selectedTagId ->
dialogFactory.convertToDialogUio(
characterSheet = characterSheet,
tagMap = tagMap,
alterations = alterations,
filter = filter,
tags = tags,
selectedTagId = selectedTagId,
)
}
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = null
)
fun show(characterSheetId: String?) {
selectedCharacterSheetIdFlow.update { characterSheetId }
}
fun hide() {
selectedCharacterSheetIdFlow.update { null }
}
fun selectedTag(id: String) {
selectedTagIdFlow.update {
when (it) {
id -> null
else -> id
}
}
}
suspend fun toggleAlteration(
characterSheetId: String,
alterationId: String,
active: Boolean,
) {
characterSheetRepository.updateAlteration(
characterSheetId = characterSheetId,
alterationId = alterationId,
active = active.not(),
)
}
}

View file

@ -4,19 +4,15 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
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.model.campaign.factory.CampaignJsonFactory
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
class CharacterDetailCharacteristicDialogViewModel(
class CharacterSheetCharacteristicDialogViewModel(
private val characterSheetRepository: CharacterSheetRepository,
private val campaignRepository: CampaignRepository,
private val alterationRepository: AlterationRepository,
private val campaignJsonFactory: CampaignJsonFactory,
private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory,
private val factory: CharacterSheetCharacteristicDialogFactory,
private val network: NetworkRepository,

View file

@ -1,4 +1,4 @@
package com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog
package com.pixelized.desktop.lwa.ui.composable.character.diminished
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -40,7 +40,7 @@ import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
import org.jetbrains.compose.resources.stringResource
@Stable
data class CharacterSheetDiminishedStatDialogUio(
data class CharacterSheetDiminishedDialogUio(
val characterSheetId: String,
val label: String,
val value: () -> TextFieldValue,
@ -48,16 +48,16 @@ data class CharacterSheetDiminishedStatDialogUio(
)
@Composable
fun DiminishedStatDialog(
dialog: State<CharacterSheetDiminishedStatDialogUio?>,
onConfirm: (CharacterSheetDiminishedStatDialogUio) -> Unit,
fun CharacterSheetDiminishedDialog(
dialog: State<CharacterSheetDiminishedDialogUio?>,
onConfirm: (CharacterSheetDiminishedDialogUio) -> Unit,
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
onDismissRequest = onDismissRequest,
content = {
DiminishedDialogContent(
CharacterSheetDiminishedContent(
dialog = it,
onConfirm = onConfirm,
onDismissRequest = onDismissRequest,
@ -68,9 +68,9 @@ fun DiminishedStatDialog(
}
@Composable
private fun DiminishedDialogContent(
dialog: CharacterSheetDiminishedStatDialogUio,
onConfirm: (CharacterSheetDiminishedStatDialogUio) -> Unit,
private fun CharacterSheetDiminishedContent(
dialog: CharacterSheetDiminishedDialogUio,
onConfirm: (CharacterSheetDiminishedDialogUio) -> Unit,
onDismissRequest: () -> Unit,
) {
val typography = MaterialTheme.typography

View file

@ -5,7 +5,6 @@ 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.screen.characterSheet.detail.dialog.CharacterSheetDiminishedStatDialogUio
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
@ -20,7 +19,7 @@ class CharacterSheetDiminishedDialogFactory(
) {
suspend fun convertToDialogUio(
characterSheetId: String?,
) : CharacterSheetDiminishedStatDialogUio? {
) : CharacterSheetDiminishedDialogUio? {
if (characterSheetId == null) return null
@ -45,7 +44,7 @@ class CharacterSheetDiminishedDialogFactory(
selection = TextRange(index = 0),
)
)
return CharacterSheetDiminishedStatDialogUio(
return CharacterSheetDiminishedDialogUio(
characterSheetId = characterSheetId,
label = getString(resource = Res.string.character_sheet__diminished__label),
value = { textFieldValue.value },

View file

@ -4,16 +4,15 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDiminishedStatDialogUio
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
class CharacterSheetDiminishedViewModel(
class CharacterSheetDiminishedDialogViewModel(
private val networkRepository: NetworkRepository,
private val factory: CharacterSheetDiminishedDialogFactory,
) : ViewModel() {
private val _diminishedDialog = mutableStateOf<CharacterSheetDiminishedStatDialogUio?>(null)
val diminishedDialog: State<CharacterSheetDiminishedStatDialogUio?> get() = _diminishedDialog
private val _diminishedDialog = mutableStateOf<CharacterSheetDiminishedDialogUio?>(null)
val diminishedDialog: State<CharacterSheetDiminishedDialogUio?> get() = _diminishedDialog
suspend fun showDiminishedDialog(
characterSheetId: String?,
@ -28,7 +27,7 @@ class CharacterSheetDiminishedViewModel(
}
suspend fun changeDiminished(
dialog: CharacterSheetDiminishedStatDialogUio,
dialog: CharacterSheetDiminishedDialogUio,
) {
val diminished = dialog.value().text.toIntOrNull() ?: 0
networkRepository.share(

View file

@ -22,9 +22,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
data class LwaTextFieldUio(
val enable: Boolean = true,
val isError: MutableStateFlow<Boolean>,
val labelFlow: MutableStateFlow<String?>?,
val valueFlow: MutableStateFlow<String>,
val placeHolderFlow: MutableStateFlow<String?>?,
val label: String?,
val placeHolder: String?,
val onValueChange: (String) -> Unit,
)
@ -46,8 +46,6 @@ fun LwaTextField(
Modifier
}
val label = field.labelFlow?.collectAsState()
val placeHolder = field.placeHolderFlow?.collectAsState()
val value = field.valueFlow.collectAsState()
val isError = field.isError.collectAsState()
@ -60,7 +58,7 @@ fun LwaTextField(
},
enabled = field.enable,
singleLine = singleLine,
placeholder = placeHolder?.value?.let {
placeholder = field.placeHolder?.let {
{
Text(
overflow = TextOverflow.Ellipsis,
@ -70,7 +68,7 @@ fun LwaTextField(
}
},
isError = isError.value,
label = label?.value?.let {
label = field.label?.let {
{
Text(
overflow = TextOverflow.Ellipsis,

View file

@ -88,7 +88,7 @@ private fun RollOverlayKeyHandler(
) {
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
it.type == KeyEventType.KeyDown && it.key == Key.Escape -> {
onDismissRequest()
true
}

View file

@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -30,15 +31,18 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialog
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialog
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToLevelScreen
import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlay
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.rememberTransitionAnimation
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.npc.NpcRibbon
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player.PlayerRibbon
@ -46,7 +50,6 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.text.CampaignChat
import com.pixelized.desktop.lwa.ui.screen.campaign.text.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbar
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbarViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
import kotlinx.coroutines.launch
import org.koin.compose.viewmodel.koinViewModel
@ -58,8 +61,9 @@ val LocalCampaignLayoutScope = compositionLocalOf<CampaignLayoutScope> {
fun CampaignScreen(
playerDetailViewModel: CharacterDetailViewModel = koinViewModel(key = "player"),
npcDetailViewModel: CharacterDetailViewModel = koinViewModel(key = "npc"),
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
dismissedViewModel: CharacterSheetDiminishedViewModel = koinViewModel(),
characteristicDialogViewModel: CharacterSheetCharacteristicDialogViewModel = koinViewModel(),
dismissedViewModel: CharacterSheetDiminishedDialogViewModel = koinViewModel(),
alterationViewModel: CharacterSheetAlterationDialogViewModel = koinViewModel(),
campaignViewModel: CampaignToolbarViewModel = koinViewModel(),
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
) {
@ -124,6 +128,7 @@ fun CampaignScreen(
detailViewModel = npcDetailViewModel,
characterDiminishedViewModel = dismissedViewModel,
characteristicDialogViewModel = characteristicDialogViewModel,
alterationViewModel = alterationViewModel,
)
},
rightPanel = {
@ -150,6 +155,7 @@ fun CampaignScreen(
detailViewModel = playerDetailViewModel,
characterDiminishedViewModel = dismissedViewModel,
characteristicDialogViewModel = characteristicDialogViewModel,
alterationViewModel = alterationViewModel,
)
},
)
@ -174,7 +180,7 @@ fun CampaignScreen(
},
)
DiminishedStatDialog(
CharacterSheetDiminishedDialog(
dialog = dismissedViewModel.diminishedDialog,
onConfirm = { diminished ->
scope.launch {
@ -192,10 +198,32 @@ fun CampaignScreen(
)
}
CharacterSheetAlterationDialog(
dialog = alterationViewModel.alterationDialog.collectAsState(),
onConfirm = { },
onTag = {
alterationViewModel.selectedTag(id = it)
},
onAlteration = { characterSheetId, alterationId, active ->
scope.launch {
alterationViewModel.toggleAlteration(characterSheetId, alterationId, active)
}
},
onDismissRequest = {
blurController.hide()
alterationViewModel.hide()
},
)
CampaignKeyHandler(
onDismissRequest = {
playerDetailViewModel.hideCharacter()
npcDetailViewModel.hideCharacter()
val noDialog = characteristicDialogViewModel.statChangeDialog.value == null
&& dismissedViewModel.diminishedDialog.value == null
&& alterationViewModel.alterationDialog.value == null
if (noDialog) {
playerDetailViewModel.hideCharacter()
npcDetailViewModel.hideCharacter()
}
}
)
}
@ -293,7 +321,7 @@ private fun CampaignKeyHandler(
) {
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
it.type == KeyEventType.KeyDown && it.key == Key.Escape -> {
onDismissRequest()
true
}

View file

@ -32,9 +32,10 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalRollHostState
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogUio
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeader
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio
@ -60,8 +61,9 @@ fun CharacterDetailPanel(
blurController: BlurContentController,
transitionSpec: AnimatedContentTransitionScope<CharacterDetailPanelUio>.() -> ContentTransform = rememberTransitionAnimation(),
detailViewModel: CharacterDetailViewModel,
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel,
characterDiminishedViewModel: CharacterSheetDiminishedViewModel,
characteristicDialogViewModel: CharacterSheetCharacteristicDialogViewModel,
characterDiminishedViewModel: CharacterSheetDiminishedDialogViewModel,
alterationViewModel: CharacterSheetAlterationDialogViewModel,
) {
val roll = LocalRollHostState.current
val scope = rememberCoroutineScope()
@ -74,6 +76,10 @@ fun CharacterDetailPanel(
onDismissRequest = {
detailViewModel.hideCharacter()
},
onAlteration = {
blurController.show()
alterationViewModel.show(characterSheetId = it)
},
onDiminished = {
scope.launch {
blurController.show()
@ -141,6 +147,7 @@ fun CharacterDetailAnimatedPanel(
detail: State<CharacterDetailPanelUio>,
transitionSpec: AnimatedContentTransitionScope<CharacterDetailPanelUio>.() -> ContentTransform,
onDismissRequest: (characterSheetId: String) -> Unit,
onAlteration: (characterSheetId: String) -> Unit,
onDiminished: (characterSheetId: String) -> Unit,
onHp: (characterSheetId: String) -> Unit,
onPp: (characterSheetId: String) -> Unit,
@ -172,6 +179,7 @@ fun CharacterDetailAnimatedPanel(
header = it.header.collectAsState(),
sheet = it.sheet.collectAsState(),
onDismissRequest = { onDismissRequest(it.characterSheetId) },
onAlteration = { onAlteration(it.characterSheetId) },
onDiminished = { onDiminished(it.characterSheetId) },
onHp = { onHp(it.characterSheetId) },
onPp = { onPp(it.characterSheetId) },
@ -194,6 +202,7 @@ fun CharacterDetailContent(
shape: Shape = MaterialTheme.lwa.shapes.panel,
header: State<CharacterDetailHeaderUio?>,
sheet: State<CharacterDetailSheetUio?>,
onAlteration: () -> Unit,
onDismissRequest: () -> Unit,
onDiminished: () -> Unit,
onHp: () -> Unit,
@ -217,6 +226,7 @@ fun CharacterDetailContent(
.fillMaxWidth(),
header = header,
onDismissRequest = onDismissRequest,
onAlteration = onAlteration,
onDiminished = onDiminished,
onHp = onHp,
onPp = onPp,
@ -242,7 +252,7 @@ fun CharacterDetailContent(
@Stable
fun rememberTransitionAnimation(
direction: LayoutDirection = LayoutDirection.Rtl,
) : AnimatedContentTransitionScope<CharacterDetailPanelUio>.() -> ContentTransform {
): AnimatedContentTransitionScope<CharacterDetailPanelUio>.() -> ContentTransform {
return remember {
val mul = if (direction == LayoutDirection.Rtl) 1 else -1
{

View file

@ -1,5 +1,6 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail
import com.pixelized.desktop.lwa.repository.settings.model.Settings
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction
@ -55,6 +56,7 @@ class CharacterDetailFactory(
suspend fun convertToCharacterDetailHeaderUio(
characterSheetId: String,
characterSheet: CharacterSheet?,
settings: Settings,
alterations: Map<String, List<FieldAlteration>>,
): CharacterDetailHeaderUio? {
if (characterSheet == null) return null
@ -71,6 +73,7 @@ class CharacterDetailFactory(
characterSheetId = characterSheetId,
portrait = alteredCharacterSheet.portrait,
diminished = alteredCharacterSheet.diminished,
alteration = settings.isAdmin ?: false,
name = alteredCharacterSheet.name,
level = alteredCharacterSheet.level,
hp = "${maxHp - alteredCharacterSheet.damage}",

View file

@ -5,6 +5,7 @@ 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.repository.settings.SettingsRepository
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@ -17,6 +18,7 @@ import kotlinx.coroutines.launch
class CharacterDetailViewModel(
private val characterSheetRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository,
settingRepository: SettingsRepository,
private val characterDetailFactory: CharacterDetailFactory,
private val network: NetworkRepository,
) : ViewModel() {
@ -32,10 +34,12 @@ class CharacterDetailViewModel(
header = combine(
characterSheetRepository.characterDetailFlow(characterSheetId = characterSheetId),
alterationRepository.fieldAlterationsFlow(characterSheetId = characterSheetId),
) { characterSheet, alterations ->
settingRepository.settingsFlow()
) { characterSheet, alterations, settings, ->
characterDetailFactory.convertToCharacterDetailHeaderUio(
characterSheetId = characterSheetId,
characterSheet = characterSheet,
settings = settings,
alterations = alterations,
)
}.stateIn(

View file

@ -1,6 +1,7 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@ -43,13 +44,13 @@ import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
import lwacharactersheet.composeapp.generated.resources.ic_cognition_24dp
import lwacharactersheet.composeapp.generated.resources.ic_heart_24dp
import lwacharactersheet.composeapp.generated.resources.ic_heart_plus_24dp
import lwacharactersheet.composeapp.generated.resources.ic_near_me
import lwacharactersheet.composeapp.generated.resources.ic_shield_24dp
import lwacharactersheet.composeapp.generated.resources.ic_skull_24dp
import lwacharactersheet.composeapp.generated.resources.ic_swords_24dp
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
import lwacharactersheet.composeapp.generated.resources.ic_pan_tool_24dp
import lwacharactersheet.composeapp.generated.resources.ic_azm_24dp
import lwacharactersheet.composeapp.generated.resources.ic_blur_on_24dp
import lwacharactersheet.composeapp.generated.resources.ic_directions_run_24dp
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
@ -59,6 +60,7 @@ data class CharacterDetailHeaderUio(
val characterSheetId: String,
val portrait: String?,
val diminished: Int,
val alteration: Boolean,
val name: String,
val level: Int,
val hp: String,
@ -91,6 +93,7 @@ fun CharacterDetailHeader(
iconSize: Dp = MaterialTheme.lwa.size.sheet.subCategory,
header: State<CharacterDetailHeaderUio?>,
onDismissRequest: () -> Unit,
onAlteration: () -> Unit,
onDiminished: () -> Unit,
onHp: () -> Unit,
onPp: () -> Unit,
@ -124,6 +127,22 @@ fun CharacterDetailHeader(
),
)
}
AnimatedVisibility(
visible = header.value?.alteration == true,
enter = fadeIn(),
exit = fadeOut(),
) {
IconButton(
onClick = onAlteration,
) {
Icon(
modifier = Modifier.size(size = 24.dp),
painter = painterResource(Res.drawable.ic_blur_on_24dp),
tint = MaterialTheme.colors.primary,
contentDescription = null,
)
}
}
Box {
IconButton(
onClick = onDiminished,

View file

@ -57,7 +57,7 @@ import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalWindowController
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
@ -69,7 +69,7 @@ import com.pixelized.desktop.lwa.ui.overlay.roll.RollPage
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio.Characteristic
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.preview.rememberCharacterSheetPreview
import com.pixelized.desktop.lwa.utils.preview.ContentPreview
import kotlinx.coroutines.launch
@ -132,7 +132,7 @@ data class CharacterSheetPageUio(
@Composable
fun CharacterSheetPage(
viewModel: CharacterSheetViewModel = koinViewModel(),
diminishedDialogViewModel: CharacterSheetDiminishedViewModel = koinViewModel(),
diminishedDialogViewModel: CharacterSheetDiminishedDialogViewModel = koinViewModel(),
rollViewModel: RollViewModel = koinViewModel(),
) {
val windowController = LocalWindowController.current
@ -248,7 +248,7 @@ fun CharacterSheetPage(
},
)
DiminishedStatDialog(
CharacterSheetDiminishedDialog(
dialog = viewModel.diminishedDialog,
onConfirm = {
scope.launch {

View file

@ -10,7 +10,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialogUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDiminishedStatDialogUio
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogUio
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
@ -35,8 +35,8 @@ class CharacterSheetViewModel(
private val _displayRollOverlay = mutableStateOf(false)
val displayRollOverlay: State<Boolean> get() = _displayRollOverlay
private val _diminishedDialog = mutableStateOf<CharacterSheetDiminishedStatDialogUio?>(null)
val diminishedDialog: State<CharacterSheetDiminishedStatDialogUio?> get() = _diminishedDialog
private val _diminishedDialog = mutableStateOf<CharacterSheetDiminishedDialogUio?>(null)
val diminishedDialog: State<CharacterSheetDiminishedDialogUio?> get() = _diminishedDialog
val sheetFlow = combine(
characterRepository.characterDetailFlow(characterSheetId = argument.characterSheetId),

View file

@ -497,21 +497,19 @@ class CharacterSheetEditFactory(
fun createLwaTextField(
enable: Boolean = true,
isError: Boolean = false,
value: String? = null,
label: String? = null,
placeholder: String? = null,
value: String? = null,
): LwaTextFieldUio {
val valueFlow = MutableStateFlow(value ?: "")
val labelFlow = MutableStateFlow(label)
val placeholderFlow = MutableStateFlow(placeholder)
val isErrorFlow = MutableStateFlow(isError)
return LwaTextFieldUio(
enable = enable,
isError = isErrorFlow,
labelFlow = labelFlow,
valueFlow = valueFlow,
placeHolderFlow = placeholderFlow,
label = label,
placeHolder = placeholder,
onValueChange = { valueFlow.value = it },
)
}

View file

@ -57,11 +57,11 @@ class CharacterSheetEditViewModel(
copyDialog.value = CharacterSheetCopyDialogUio(
label = getString(Res.string.character_sheet_edit__copy__title),
value = LwaTextFieldUio(
labelFlow = MutableStateFlow(getString(Res.string.character_sheet_edit__copy__label)),
label = getString(Res.string.character_sheet_edit__copy__label),
isError = error,
valueFlow = characterSheetId,
placeHolderFlow = MutableStateFlow(null),
onValueChange = { characterSheetId.value = it },
placeHolder = null,
onValueChange = { characterSheetId.value = it }
),
validate = {
characterSheetRepository.checkCharacterSheetIdValidity(

View file

@ -40,25 +40,25 @@ class GMAlterationEditFactory(
id = LwaTextFieldUio(
enable = originId == null,
isError = MutableStateFlow(false),
labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_id)),
label = getString(Res.string.game_master__alteration__edit_id),
valueFlow = idFlow,
placeHolderFlow = null,
placeHolder = null,
onValueChange = { idFlow.value = it },
),
label = LwaTextFieldUio(
enable = true,
isError = MutableStateFlow(false),
labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_label)),
label = getString(Res.string.game_master__alteration__edit_label),
valueFlow = labelFlow,
placeHolderFlow = null,
placeHolder = null,
onValueChange = { labelFlow.value = it },
),
description = LwaTextFieldUio(
enable = true,
isError = MutableStateFlow(false),
labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_description)),
label = getString(Res.string.game_master__alteration__edit_description),
valueFlow = descriptionFlow,
placeHolderFlow = null,
placeHolder = null,
onValueChange = { descriptionFlow.value = it },
),
tags = tagFlow,
@ -77,17 +77,17 @@ class GMAlterationEditFactory(
id = LwaTextFieldUio(
enable = true,
isError = MutableStateFlow(false),
labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_field_id)),
label = getString(Res.string.game_master__alteration__edit_field_id),
valueFlow = idFlow,
placeHolderFlow = null,
placeHolder = null,
onValueChange = { idFlow.value = it },
),
expression = LwaTextFieldUio(
enable = true,
isError = MutableStateFlow(false),
labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_field_expression)),
label = getString(Res.string.game_master__alteration__edit_field_expression),
valueFlow = expressionFlow,
placeHolderFlow = null,
placeHolder = null,
onValueChange = { expressionFlow.value = it },
)
)

View file

@ -365,7 +365,7 @@ private fun AlterationEditKeyHandler(
) {
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
it.type == KeyEventType.KeyDown && it.key == Key.Escape -> {
onDismissRequest()
true
}

View file

@ -32,10 +32,10 @@ class GMAlterationViewModel(
val filter = LwaTextFieldUio(
enable = true,
labelFlow = MutableStateFlow(runBlocking { getString(Res.string.game_master__character__filter) }),
label = runBlocking { getString(Res.string.game_master__character__filter) },
valueFlow = filterValue,
isError = MutableStateFlow(false),
placeHolderFlow = MutableStateFlow(null),
placeHolder = null,
onValueChange = { filterValue.value = it },
)

View file

@ -34,16 +34,18 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalBlurController
import com.pixelized.desktop.lwa.LocalWindowController
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialog
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialog
import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.rememberTransitionAnimation
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMFilterHeader
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors
@ -60,8 +62,9 @@ import org.koin.compose.viewmodel.koinViewModel
fun GMCharacterPage(
viewModel: GMCharacterViewModel = koinViewModel(),
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
dismissedViewModel: CharacterSheetDiminishedViewModel = koinViewModel(),
characteristicDialogViewModel: CharacterSheetCharacteristicDialogViewModel = koinViewModel(),
dismissedViewModel: CharacterSheetDiminishedDialogViewModel = koinViewModel(),
alterationViewModel: CharacterSheetAlterationDialogViewModel = koinViewModel(),
) {
val windows = LocalWindowController.current
val blurController = LocalBlurController.current
@ -111,6 +114,7 @@ fun GMCharacterPage(
detailViewModel = characterDetailViewModel,
characterDiminishedViewModel = dismissedViewModel,
characteristicDialogViewModel = characteristicDialogViewModel,
alterationViewModel = alterationViewModel,
)
CharacterSheetCharacteristicDialog(
@ -132,7 +136,7 @@ fun GMCharacterPage(
},
)
DiminishedStatDialog(
CharacterSheetDiminishedDialog(
dialog = dismissedViewModel.diminishedDialog,
onConfirm = { diminished ->
scope.launch {
@ -148,6 +152,23 @@ fun GMCharacterPage(
dismissedViewModel.hideDiminishedDialog()
},
)
CharacterSheetAlterationDialog(
dialog = alterationViewModel.alterationDialog.collectAsState(),
onConfirm = { },
onTag = {
alterationViewModel.selectedTag(id = it)
},
onAlteration = { characterSheetId, alterationId, active ->
scope.launch {
alterationViewModel.toggleAlteration(characterSheetId, alterationId, active)
}
},
onDismissRequest = {
blurController.hide()
alterationViewModel.hide()
},
)
}
GameMasterCharacterKeyHandler(
@ -253,7 +274,7 @@ private fun GameMasterCharacterKeyHandler(
) {
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
it.type == KeyEventType.KeyDown && it.key == Key.Escape -> {
onDismissRequest()
true
}

View file

@ -22,7 +22,6 @@ import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.game_master__character__filter
import org.jetbrains.compose.resources.getString
import java.text.Collator
class GMCharacterViewModel(
private val networkRepository: NetworkRepository,
@ -38,10 +37,10 @@ class GMCharacterViewModel(
val filter = LwaTextFieldUio(
enable = true,
labelFlow = MutableStateFlow(runBlocking { getString(Res.string.game_master__character__filter) }),
label = runBlocking { getString(Res.string.game_master__character__filter) },
valueFlow = filterValue,
isError = MutableStateFlow(false),
placeHolderFlow = MutableStateFlow(null),
placeHolder = null,
onValueChange = { filterValue.value = it },
)

View file

@ -24,12 +24,14 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTag
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
import com.pixelized.desktop.lwa.ui.theme.color.component.LwaTextFieldColors
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
@ -54,6 +56,7 @@ fun GMFilterHeader(
LwaTextField(
modifier = Modifier.fillMaxWidth(),
field = filter,
colors = LwaTextFieldColors(backgroundColor = Color.Transparent),
trailingIcon = {
val value = filter.valueFlow.collectAsState()
AnimatedVisibility(

View file

@ -28,9 +28,10 @@ class GMTagFactory {
): List<GMTagUio> {
return tags
.map { tag ->
convertToGMTagItemUio(
tag = tag,
selectedTagId = selectedTagId,
GMTagUio(
id = tag.id,
label = tag.label,
highlight = tag.id == selectedTagId,
)
}
.sortedWith(

View file

@ -411,7 +411,7 @@ private fun LevelUpKeyHandler(
) {
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
it.type == KeyEventType.KeyDown && it.key == Key.Escape -> {
onDismissRequest()
true
}

View file

@ -132,7 +132,7 @@ private fun SettingsKeyHandler(
) {
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
it.type == KeyEventType.KeyDown && it.key == Key.Escape -> {
onDismissRequest()
true
}

View file

@ -17,4 +17,16 @@ fun LwaTextFieldColors(
backgroundColor = colors.elevated.base1dp,
disabledIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
)
@Composable
@Stable
fun LwaTextFieldColors(
backgroundColor: Color = MaterialTheme.lwa.colorScheme.elevated.base1dp,
disabledIndicatorColor: Color = Color.Transparent,
unfocusedIndicatorColor: Color = Color.Transparent,
): TextFieldColors = TextFieldDefaults.textFieldColors(
backgroundColor = backgroundColor,
disabledIndicatorColor = disabledIndicatorColor,
unfocusedIndicatorColor = unfocusedIndicatorColor,
)