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__section">Portrait joueur</string>
|
||||||
<string name="settings__player_portrait__dyn_dice_tile">Dés dynamiques</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_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__section">Chatlog options</string>
|
||||||
<string name="settings__chat_log__auto_show_title">Afficher automatiquement le chat</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>
|
<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,
|
port = settings.network.port,
|
||||||
playerName = settings.playerName,
|
playerName = settings.playerName,
|
||||||
dynamicDice = settings.portrait.dynamicDice,
|
dynamicDice = settings.portrait.dynamicDice,
|
||||||
|
dynamicDiceDelay = settings.portrait.dynamicDiceDelay,
|
||||||
autoHideChat = settings.chat.autoHideChat,
|
autoHideChat = settings.chat.autoHideChat,
|
||||||
autoHideDelay = settings.chat.autoHideDelay,
|
autoHideDelay = settings.chat.autoHideDelay,
|
||||||
autoShowChat = settings.chat.autoShowChat,
|
autoShowChat = settings.chat.autoShowChat,
|
||||||
|
|
@ -47,6 +48,7 @@ class SettingsFactory(
|
||||||
),
|
),
|
||||||
portrait = Settings.Portrait(
|
portrait = Settings.Portrait(
|
||||||
dynamicDice = json.dynamicDice ?: default.portrait.dynamicDice,
|
dynamicDice = json.dynamicDice ?: default.portrait.dynamicDice,
|
||||||
|
dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay,
|
||||||
),
|
),
|
||||||
chat = Settings.Chat(
|
chat = Settings.Chat(
|
||||||
autoHideChat = json.autoHideChat ?: default.chat.autoHideChat,
|
autoHideChat = json.autoHideChat ?: default.chat.autoHideChat,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ data class Settings(
|
||||||
) {
|
) {
|
||||||
data class Portrait(
|
data class Portrait(
|
||||||
val dynamicDice: Boolean,
|
val dynamicDice: Boolean,
|
||||||
|
val dynamicDiceDelay: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Chat(
|
data class Chat(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ data class SettingsJsonV1(
|
||||||
val port: Int?,
|
val port: Int?,
|
||||||
val playerName: String?,
|
val playerName: String?,
|
||||||
val dynamicDice: Boolean?,
|
val dynamicDice: Boolean?,
|
||||||
|
val dynamicDiceDelay: Int?,
|
||||||
val autoHideChat: Boolean?,
|
val autoHideChat: Boolean?,
|
||||||
val autoHideDelay: Int?,
|
val autoHideDelay: Int?,
|
||||||
val autoShowChat: Boolean?,
|
val autoShowChat: Boolean?,
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,8 @@ abstract class CharacterRibbonViewModel(
|
||||||
) { settings, roll ->
|
) { settings, roll ->
|
||||||
if (settings.portrait.dynamicDice && characterSheetId == roll.characterSheetId) {
|
if (settings.portrait.dynamicDice && characterSheetId == roll.characterSheetId) {
|
||||||
state.value = CharacterRibbonRollUio(
|
state.value = CharacterRibbonRollUio(
|
||||||
|
rollId = roll.uuid,
|
||||||
|
hideDelay = settings.portrait.dynamicDiceDelay,
|
||||||
characterSheetId = characterSheetId,
|
characterSheetId = characterSheetId,
|
||||||
value = roll.rollValue,
|
value = roll.rollValue,
|
||||||
tint = when (roll.critical) {
|
tint = when (roll.critical) {
|
||||||
|
|
@ -153,10 +155,4 @@ abstract class CharacterRibbonViewModel(
|
||||||
|
|
||||||
return state
|
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.animateColorAsState
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.Animatable
|
||||||
import androidx.compose.animation.core.AnimationVector1D
|
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.spring
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
|
|
@ -40,16 +42,29 @@ import org.jetbrains.compose.resources.painterResource
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class CharacterRibbonRollUio(
|
data class CharacterRibbonRollUio(
|
||||||
|
val rollId: String,
|
||||||
|
val hideDelay: Int,
|
||||||
val characterSheetId: String,
|
val characterSheetId: String,
|
||||||
val value: Int?,
|
val value: Int?,
|
||||||
val tint: Color?,
|
val tint: Color?,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class CharacterRibbonRollAnimation(
|
class CharacterRibbonRollAnimation(
|
||||||
val rotation: Animatable<Float, AnimationVector1D> = Animatable(0f),
|
val animatedAlpha: Animatable<Float, AnimationVector1D>,
|
||||||
val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
|
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
|
@Composable
|
||||||
fun CharacterRibbonRoll(
|
fun CharacterRibbonRoll(
|
||||||
|
|
@ -69,13 +84,17 @@ fun CharacterRibbonRoll(
|
||||||
enter togetherWith exit using SizeTransform(clip = false)
|
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)
|
val color = animateColorAsState(targetValue = it?.tint ?: Color.Transparent)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.graphicsLayer {
|
modifier = Modifier.graphicsLayer {
|
||||||
this.scaleX = animation.scale.value
|
this.alpha = animation.animatedAlpha.value
|
||||||
this.scaleY = animation.scale.value
|
this.scaleX = animation.animatedScale.value
|
||||||
|
this.scaleY = animation.animatedScale.value
|
||||||
},
|
},
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
|
|
@ -83,7 +102,7 @@ fun CharacterRibbonRoll(
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
this.rotationZ = animation.rotation.value
|
this.rotationZ = animation.animatedRotation.value
|
||||||
}
|
}
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(1f)
|
.aspectRatio(1f)
|
||||||
|
|
@ -111,20 +130,23 @@ fun CharacterRibbonRoll(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun diceIconAnimation(key: Any = Unit): CharacterRibbonRollAnimation {
|
private fun diceIconAnimation(
|
||||||
|
key: Any = Unit,
|
||||||
|
rollDelay: Int,
|
||||||
|
): CharacterRibbonRollAnimation {
|
||||||
val animation = remember(key) {
|
val animation = remember(key) {
|
||||||
CharacterRibbonRollAnimation()
|
CharacterRibbonRollAnimation()
|
||||||
}
|
}
|
||||||
LaunchedEffect(key) {
|
LaunchedEffect(key) {
|
||||||
launch {
|
launch {
|
||||||
animation.scale.animateTo(
|
animation.animatedScale.animateTo(
|
||||||
targetValue = 1.20f,
|
targetValue = 1.20f,
|
||||||
animationSpec = spring(
|
animationSpec = spring(
|
||||||
dampingRatio = Spring.DampingRatioNoBouncy,
|
dampingRatio = Spring.DampingRatioNoBouncy,
|
||||||
stiffness = 800f,
|
stiffness = 800f,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
animation.scale.animateTo(
|
animation.animatedScale.animateTo(
|
||||||
targetValue = 1f,
|
targetValue = 1f,
|
||||||
animationSpec = spring(
|
animationSpec = spring(
|
||||||
dampingRatio = 0.28f,
|
dampingRatio = 0.28f,
|
||||||
|
|
@ -133,7 +155,7 @@ private fun diceIconAnimation(key: Any = Unit): CharacterRibbonRollAnimation {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
animation.rotation.animateTo(
|
animation.animatedRotation.animateTo(
|
||||||
targetValue = 360f * 3,
|
targetValue = 360f * 3,
|
||||||
animationSpec = spring(
|
animationSpec = spring(
|
||||||
dampingRatio = Spring.DampingRatioNoBouncy,
|
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
|
return animation
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.material.TopAppBar
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.key.Key
|
import androidx.compose.ui.input.key.Key
|
||||||
import androidx.compose.ui.input.key.KeyEventType
|
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.jetbrains.compose.resources.stringResource
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
|
@Stable
|
||||||
object SettingsScreenDefault {
|
object SettingsScreenDefault {
|
||||||
|
@Stable
|
||||||
val margin: PaddingValues = PaddingValues(horizontal = 16.dp)
|
val margin: PaddingValues = PaddingValues(horizontal = 16.dp)
|
||||||
|
@Stable
|
||||||
val padding: PaddingValues = PaddingValues(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp)
|
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_scroll_title
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_show_description
|
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__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__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_description
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__player_portrait__dyn_dice_tile
|
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__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(
|
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(
|
SettingSectionUio(
|
||||||
title = Res.string.settings__chat_log__section,
|
title = Res.string.settings__chat_log__section,
|
||||||
),
|
),
|
||||||
|
|
@ -138,6 +169,7 @@ class SettingsViewModel(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
settingsRepository.settingsFlow().collect { settings ->
|
settingsRepository.settingsFlow().collect { settings ->
|
||||||
booleanStates.dynamicDice.value = settings.portrait.dynamicDice
|
booleanStates.dynamicDice.value = settings.portrait.dynamicDice
|
||||||
|
intStates.dynamicDiceDelay.value = settings.portrait.dynamicDiceDelay
|
||||||
booleanStates.autoShowChat.value = settings.chat.autoShowChat
|
booleanStates.autoShowChat.value = settings.chat.autoShowChat
|
||||||
booleanStates.autoHideChat.value = settings.chat.autoHideChat
|
booleanStates.autoHideChat.value = settings.chat.autoHideChat
|
||||||
intStates.autoHideDelay.value = settings.chat.autoHideDelay
|
intStates.autoHideDelay.value = settings.chat.autoHideDelay
|
||||||
|
|
@ -162,6 +194,9 @@ class SettingsViewModel(
|
||||||
private val HashMap<String, MutableState<Boolean>>.dynamicDice
|
private val HashMap<String, MutableState<Boolean>>.dynamicDice
|
||||||
get() = getOrPut("DYNAMIC_DICE") { mutableStateOf(settings.value.portrait.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
|
private val HashMap<String, MutableState<Boolean>>.autoShowChat
|
||||||
get() = getOrPut("AUTO_SHOW_CHAT") { mutableStateOf(settings.value.chat.autoShowChat) }
|
get() = getOrPut("AUTO_SHOW_CHAT") { mutableStateOf(settings.value.chat.autoShowChat) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ fun SettingNumberItem(
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.onFocusChanged { focused.value = it.isFocused }
|
.onFocusChanged { focused.value = it.isFocused }
|
||||||
.width(width = 44.dp)
|
.width(width = 64.dp)
|
||||||
.padding(horizontal = 2.dp),
|
.padding(horizontal = 2.dp),
|
||||||
textStyle = MaterialTheme.lwa.typography.settings.input.copy(
|
textStyle = MaterialTheme.lwa.typography.settings.input.copy(
|
||||||
color = textColor.value,
|
color = textColor.value,
|
||||||
|
|
@ -129,10 +129,9 @@ fun SettingNumberItem(
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(color = borderColor.value)
|
.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(
|
fun defaultSettings(): Settings = Settings(
|
||||||
playerName = "",
|
playerName = "",
|
||||||
portrait = Settings.Portrait(
|
portrait = Settings.Portrait(
|
||||||
dynamicDice = true
|
dynamicDice = true,
|
||||||
|
dynamicDiceDelay = 5000,
|
||||||
),
|
),
|
||||||
chat = Settings.Chat(
|
chat = Settings.Chat(
|
||||||
autoHideChat = true,
|
autoHideChat = true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue