diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index c070d7e..5f5b95b 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -262,6 +262,8 @@ Portrait joueur Dés dynamiques Affiche un dé à côté du portrait d'un personnage lorsqu'un jet est fait par ce dernier. + Délai pour les Dés dynamiques + Délai après lequel les dés dynamiques disparaissent. Chatlog options Afficher automatiquement le chat Affiche automatiquement le chat lors de la réception d'un message diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt index 9852842..8924ff2 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt @@ -17,6 +17,7 @@ class SettingsFactory( port = settings.network.port, playerName = settings.playerName, dynamicDice = settings.portrait.dynamicDice, + dynamicDiceDelay = settings.portrait.dynamicDiceDelay, autoHideChat = settings.chat.autoHideChat, autoHideDelay = settings.chat.autoHideDelay, autoShowChat = settings.chat.autoShowChat, @@ -47,6 +48,7 @@ class SettingsFactory( ), portrait = Settings.Portrait( dynamicDice = json.dynamicDice ?: default.portrait.dynamicDice, + dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay, ), chat = Settings.Chat( autoHideChat = json.autoHideChat ?: default.chat.autoHideChat, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt index 4dcec1d..80fc9ee 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt @@ -10,6 +10,7 @@ data class Settings( ) { data class Portrait( val dynamicDice: Boolean, + val dynamicDiceDelay: Int, ) data class Chat( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt index 386314f..1f942d4 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt @@ -8,6 +8,7 @@ data class SettingsJsonV1( val port: Int?, val playerName: String?, val dynamicDice: Boolean?, + val dynamicDiceDelay: Int?, val autoHideChat: Boolean?, val autoHideDelay: Int?, val autoShowChat: Boolean?, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/CharacterRibbonViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/CharacterRibbonViewModel.kt index 78fab0b..36f0bca 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/CharacterRibbonViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/CharacterRibbonViewModel.kt @@ -136,6 +136,8 @@ abstract class CharacterRibbonViewModel( ) { settings, roll -> if (settings.portrait.dynamicDice && characterSheetId == roll.characterSheetId) { state.value = CharacterRibbonRollUio( + rollId = roll.uuid, + hideDelay = settings.portrait.dynamicDiceDelay, characterSheetId = characterSheetId, value = roll.rollValue, tint = when (roll.critical) { @@ -153,10 +155,4 @@ abstract class CharacterRibbonViewModel( return state } - - fun onPortraitRollRightClick( - characterSheetId: String, - ) { - rolls[characterSheetId]?.value = null - } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/common/CharacterRibbonRoll.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/common/CharacterRibbonRoll.kt index b049735..259939f 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/common/CharacterRibbonRoll.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/common/CharacterRibbonRoll.kt @@ -5,8 +5,10 @@ import androidx.compose.animation.SizeTransform import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith @@ -40,16 +42,29 @@ import org.jetbrains.compose.resources.painterResource @Stable data class CharacterRibbonRollUio( + val rollId: String, + val hideDelay: Int, val characterSheetId: String, val value: Int?, val tint: Color?, ) @Stable -data class CharacterRibbonRollAnimation( - val rotation: Animatable = Animatable(0f), - val scale: Animatable = Animatable(1f), -) +class CharacterRibbonRollAnimation( + val animatedAlpha: Animatable, + val animatedRotation: Animatable, + val animatedScale: Animatable, +) { + constructor( + alpha: Float = 1f, + rotation: Float = 0f, + scale: Float = 1f, + ) : this( + animatedAlpha = Animatable(alpha), + animatedRotation = Animatable(rotation), + animatedScale = Animatable(scale), + ) +} @Composable fun CharacterRibbonRoll( @@ -69,13 +84,17 @@ fun CharacterRibbonRoll( enter togetherWith exit using SizeTransform(clip = false) } ) { - val animation = diceIconAnimation(key = it ?: Unit) + val animation = diceIconAnimation( + key = it?.rollId ?: Unit, + rollDelay = it?.hideDelay ?: 1000, + ) val color = animateColorAsState(targetValue = it?.tint ?: Color.Transparent) Box( modifier = Modifier.graphicsLayer { - this.scaleX = animation.scale.value - this.scaleY = animation.scale.value + this.alpha = animation.animatedAlpha.value + this.scaleX = animation.animatedScale.value + this.scaleY = animation.animatedScale.value }, contentAlignment = Alignment.Center, ) { @@ -83,7 +102,7 @@ fun CharacterRibbonRoll( Icon( modifier = Modifier .graphicsLayer { - this.rotationZ = animation.rotation.value + this.rotationZ = animation.animatedRotation.value } .fillMaxWidth() .aspectRatio(1f) @@ -111,20 +130,23 @@ fun CharacterRibbonRoll( } @Composable -private fun diceIconAnimation(key: Any = Unit): CharacterRibbonRollAnimation { +private fun diceIconAnimation( + key: Any = Unit, + rollDelay: Int, +): CharacterRibbonRollAnimation { val animation = remember(key) { CharacterRibbonRollAnimation() } LaunchedEffect(key) { launch { - animation.scale.animateTo( + animation.animatedScale.animateTo( targetValue = 1.20f, animationSpec = spring( dampingRatio = Spring.DampingRatioNoBouncy, stiffness = 800f, ) ) - animation.scale.animateTo( + animation.animatedScale.animateTo( targetValue = 1f, animationSpec = spring( dampingRatio = 0.28f, @@ -133,7 +155,7 @@ private fun diceIconAnimation(key: Any = Unit): CharacterRibbonRollAnimation { ) } launch { - animation.rotation.animateTo( + animation.animatedRotation.animateTo( targetValue = 360f * 3, animationSpec = spring( dampingRatio = Spring.DampingRatioNoBouncy, @@ -141,6 +163,16 @@ private fun diceIconAnimation(key: Any = Unit): CharacterRibbonRollAnimation { ) ) } + launch { + animation.animatedAlpha.animateTo( + targetValue = 0f, + animationSpec = tween( + delayMillis = rollDelay, + durationMillis = 2000, + easing = FastOutSlowInEasing, + ) + ) + } } return animation } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt index b3ff792..771a2d6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEventType @@ -42,8 +43,11 @@ import lwacharactersheet.composeapp.generated.resources.settings__title import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel +@Stable object SettingsScreenDefault { + @Stable val margin: PaddingValues = PaddingValues(horizontal = 16.dp) + @Stable val padding: PaddingValues = PaddingValues(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt index bc72c57..6560bb1 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt @@ -25,12 +25,14 @@ import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_title import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_show_description import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_show_title +import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_description +import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_title import lwacharactersheet.composeapp.generated.resources.settings__chat_log__section +import lwacharactersheet.composeapp.generated.resources.settings__player_portrait__dyn_dice_delay_description +import lwacharactersheet.composeapp.generated.resources.settings__player_portrait__dyn_dice_delay_tile import lwacharactersheet.composeapp.generated.resources.settings__player_portrait__dyn_dice_description import lwacharactersheet.composeapp.generated.resources.settings__player_portrait__dyn_dice_tile import lwacharactersheet.composeapp.generated.resources.settings__player_portrait__section -import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_title -import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_description class SettingsViewModel( @@ -58,6 +60,35 @@ class SettingsViewModel( ) }, ), + SettingNumberItemUio( + icon = Res.drawable.ic_timer_24dp, + title = Res.string.settings__player_portrait__dyn_dice_delay_tile, + description = Res.string.settings__player_portrait__dyn_dice_delay_description, + enable = booleanStates.dynamicDice, + value = intStates.dynamicDiceDelay, + onValueChange = { + val range = 1000..99999 + if (it < range.first) { + settingsRepository.update( + settings = settings.value.copy( + portrait = settings.value.portrait.copy(dynamicDiceDelay = range.first) + ) + ) + } else if (it in range) { + settingsRepository.update( + settings = settings.value.copy( + portrait = settings.value.portrait.copy(dynamicDiceDelay = it) + ) + ) + } else { + settingsRepository.update( + settings = settings.value.copy( + portrait = settings.value.portrait.copy(dynamicDiceDelay = range.last) + ) + ) + } + } + ), SettingSectionUio( title = Res.string.settings__chat_log__section, ), @@ -138,6 +169,7 @@ class SettingsViewModel( viewModelScope.launch { settingsRepository.settingsFlow().collect { settings -> booleanStates.dynamicDice.value = settings.portrait.dynamicDice + intStates.dynamicDiceDelay.value = settings.portrait.dynamicDiceDelay booleanStates.autoShowChat.value = settings.chat.autoShowChat booleanStates.autoHideChat.value = settings.chat.autoHideChat intStates.autoHideDelay.value = settings.chat.autoHideDelay @@ -162,6 +194,9 @@ class SettingsViewModel( private val HashMap>.dynamicDice get() = getOrPut("DYNAMIC_DICE") { mutableStateOf(settings.value.portrait.dynamicDice) } + private val HashMap>.dynamicDiceDelay + get() = getOrPut("DYNAMIC_DICE_DELAY") { mutableStateOf(settings.value.portrait.dynamicDiceDelay) } + private val HashMap>.autoShowChat get() = getOrPut("AUTO_SHOW_CHAT") { mutableStateOf(settings.value.chat.autoShowChat) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingNumberItem.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingNumberItem.kt index dbbd591..79f3617 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingNumberItem.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingNumberItem.kt @@ -114,7 +114,7 @@ fun SettingNumberItem( BasicTextField( modifier = Modifier .onFocusChanged { focused.value = it.isFocused } - .width(width = 44.dp) + .width(width = 64.dp) .padding(horizontal = 2.dp), textStyle = MaterialTheme.lwa.typography.settings.input.copy( color = textColor.value, @@ -129,10 +129,9 @@ fun SettingNumberItem( Box( modifier = Modifier .background(color = borderColor.value) - .size(width = 48.dp, height = 2.dp), + .size(width = 64.dp, height = 2.dp), ) } } } - } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt index c1f1d63..357b6ab 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt @@ -7,7 +7,8 @@ class SettingsUseCase { fun defaultSettings(): Settings = Settings( playerName = "", portrait = Settings.Portrait( - dynamicDice = true + dynamicDice = true, + dynamicDiceDelay = 5000, ), chat = Settings.Chat( autoHideChat = true,