Add a fadeout delay to the ribbon dice.
This commit is contained in:
parent
7e1ce3ba01
commit
06c5802d7a
10 changed files with 97 additions and 24 deletions
|
|
@ -262,6 +262,8 @@
|
|||
<string name="settings__player_portrait__section">Portrait joueur</string>
|
||||
<string name="settings__player_portrait__dyn_dice_tile">Dés dynamiques</string>
|
||||
<string name="settings__player_portrait__dyn_dice_description">Affiche un dé à côté du portrait d'un personnage lorsqu'un jet est fait par ce dernier.</string>
|
||||
<string name="settings__player_portrait__dyn_dice_delay_tile">Délai pour les Dés dynamiques</string>
|
||||
<string name="settings__player_portrait__dyn_dice_delay_description">Délai après lequel les dés dynamiques disparaissent.</string>
|
||||
<string name="settings__chat_log__section">Chatlog options</string>
|
||||
<string name="settings__chat_log__auto_show_title">Afficher automatiquement le chat</string>
|
||||
<string name="settings__chat_log__auto_show_description">Affiche automatiquement le chat lors de la réception d'un message</string>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ data class Settings(
|
|||
) {
|
||||
data class Portrait(
|
||||
val dynamicDice: Boolean,
|
||||
val dynamicDiceDelay: Int,
|
||||
)
|
||||
|
||||
data class Chat(
|
||||
|
|
|
|||
|
|
@ -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?,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Float, AnimationVector1D> = Animatable(0f),
|
||||
val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
|
||||
)
|
||||
class CharacterRibbonRollAnimation(
|
||||
val animatedAlpha: Animatable<Float, AnimationVector1D>,
|
||||
val animatedRotation: Animatable<Float, AnimationVector1D>,
|
||||
val animatedScale: Animatable<Float, AnimationVector1D>,
|
||||
) {
|
||||
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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String, MutableState<Boolean>>.dynamicDice
|
||||
get() = getOrPut("DYNAMIC_DICE") { mutableStateOf(settings.value.portrait.dynamicDice) }
|
||||
|
||||
private val HashMap<String, MutableState<Int>>.dynamicDiceDelay
|
||||
get() = getOrPut("DYNAMIC_DICE_DELAY") { mutableStateOf(settings.value.portrait.dynamicDiceDelay) }
|
||||
|
||||
private val HashMap<String, MutableState<Boolean>>.autoShowChat
|
||||
get() = getOrPut("AUTO_SHOW_CHAT") { mutableStateOf(settings.value.chat.autoShowChat) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue