Refactor the chat to add a collapsable state.
This commit is contained in:
parent
651d05a7c4
commit
fc06e3ef95
11 changed files with 246 additions and 195 deletions
|
@ -0,0 +1,7 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 -960 960 960"
|
||||||
|
width="24px"
|
||||||
|
fill="#5f6368">
|
||||||
|
<path d="M200-200v-440h80v360h360v80H200Zm200-200v-440h80v360h360v80H400Z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 212 B |
|
@ -269,12 +269,6 @@
|
||||||
<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_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__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_description">Affiche automatiquement le chat lors de la réception d'un message</string>
|
|
||||||
<string name="settings__chat_log__auto_hide_title">Cacher automatiquement le chat</string>
|
|
||||||
<string name="settings__chat_log__auto_hide_description">Cache automatiquement le chat au bout d'un certain temps</string>
|
|
||||||
<string name="settings__chat_log__auto_hide_delay_title">Délai pour cacher le chat</string>
|
|
||||||
<string name="settings__chat_log__auto_hide_delay_description">Délai après lequel le chat disparaît</string>
|
|
||||||
<string name="settings__chat_log__auto_scroll_title">Défilement automatique</string>
|
<string name="settings__chat_log__auto_scroll_title">Défilement automatique</string>
|
||||||
<string name="settings__chat_log__auto_scroll_description">Défilement automatique du chat vers le dernier message reçu lors de la réception de ce dernier.</string>
|
<string name="settings__chat_log__auto_scroll_description">Défilement automatique du chat vers le dernier message reçu lors de la réception de ce dernier.</string>
|
||||||
<string name="settings__chat_log__line_count_title">Nombre de lignes de textes visibles</string>
|
<string name="settings__chat_log__line_count_title">Nombre de lignes de textes visibles</string>
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.pixelized.desktop.lwa.repository.settings
|
||||||
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
import com.pixelized.desktop.lwa.repository.settings.model.Settings
|
||||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
|
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
|
||||||
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV1
|
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV1
|
||||||
|
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV2
|
||||||
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
|
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,15 +13,13 @@ class SettingsFactory(
|
||||||
fun convertToJson(
|
fun convertToJson(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
): SettingsJson {
|
): SettingsJson {
|
||||||
return SettingsJsonV1(
|
return SettingsJsonV2(
|
||||||
host = settings.network.host,
|
host = settings.network.host,
|
||||||
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,
|
dynamicDiceDelay = settings.portrait.dynamicDiceDelay,
|
||||||
autoHideChat = settings.chat.autoHideChat,
|
showChat = settings.chat.showChat,
|
||||||
autoHideDelay = settings.chat.autoHideDelay,
|
|
||||||
autoShowChat = settings.chat.autoShowChat,
|
|
||||||
autoScrollChat = settings.chat.autoScrollChat,
|
autoScrollChat = settings.chat.autoScrollChat,
|
||||||
maxLineCount = settings.chat.maxLineCount,
|
maxLineCount = settings.chat.maxLineCount,
|
||||||
isAdmin = settings.isAdmin,
|
isAdmin = settings.isAdmin,
|
||||||
|
@ -33,6 +32,7 @@ class SettingsFactory(
|
||||||
): Settings {
|
): Settings {
|
||||||
return when (json) {
|
return when (json) {
|
||||||
is SettingsJsonV1 -> convertFromJsonV1(json)
|
is SettingsJsonV1 -> convertFromJsonV1(json)
|
||||||
|
is SettingsJsonV2 -> convertFromJsonV2(json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +51,31 @@ class SettingsFactory(
|
||||||
dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay,
|
dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay,
|
||||||
),
|
),
|
||||||
chat = Settings.Chat(
|
chat = Settings.Chat(
|
||||||
autoHideChat = json.autoHideChat ?: default.chat.autoHideChat,
|
showChat = default.chat.showChat,
|
||||||
autoHideDelay = json.autoHideDelay ?: default.chat.autoHideDelay,
|
autoScrollChat = json.autoScrollChat ?: default.chat.autoScrollChat,
|
||||||
autoShowChat = json.autoShowChat ?: default.chat.autoShowChat,
|
maxLineCount = json.maxLineCount ?: default.chat.maxLineCount,
|
||||||
|
),
|
||||||
|
isAdmin = json.isAdmin ?: default.isAdmin,
|
||||||
|
isGameMaster = json.isGameMaster ?: default.isGameMaster,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertFromJsonV2(
|
||||||
|
json: SettingsJsonV2,
|
||||||
|
): Settings {
|
||||||
|
val default = useCase.defaultSettings()
|
||||||
|
return Settings(
|
||||||
|
playerName = json.playerName ?: default.playerName,
|
||||||
|
network = Settings.Network(
|
||||||
|
host = json.host ?: default.network.host,
|
||||||
|
port = json.port ?: default.network.port,
|
||||||
|
),
|
||||||
|
portrait = Settings.Portrait(
|
||||||
|
dynamicDice = json.dynamicDice ?: default.portrait.dynamicDice,
|
||||||
|
dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay,
|
||||||
|
),
|
||||||
|
chat = Settings.Chat(
|
||||||
|
showChat = json.showChat ?: default.chat.showChat,
|
||||||
autoScrollChat = json.autoScrollChat ?: default.chat.autoScrollChat,
|
autoScrollChat = json.autoScrollChat ?: default.chat.autoScrollChat,
|
||||||
maxLineCount = json.maxLineCount ?: default.chat.maxLineCount,
|
maxLineCount = json.maxLineCount ?: default.chat.maxLineCount,
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,9 +14,7 @@ data class Settings(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Chat(
|
data class Chat(
|
||||||
val autoHideChat: Boolean,
|
val showChat: Boolean,
|
||||||
val autoHideDelay: Int,
|
|
||||||
val autoShowChat: Boolean,
|
|
||||||
val autoScrollChat: Boolean,
|
val autoScrollChat: Boolean,
|
||||||
val maxLineCount: Int,
|
val maxLineCount: Int,
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.pixelized.desktop.lwa.repository.settings.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SettingsJsonV2(
|
||||||
|
val host: String?,
|
||||||
|
val port: Int?,
|
||||||
|
val playerName: String?,
|
||||||
|
val dynamicDice: Boolean?,
|
||||||
|
val dynamicDiceDelay: Int?,
|
||||||
|
val showChat: Boolean?,
|
||||||
|
val autoScrollChat: Boolean?,
|
||||||
|
val maxLineCount: Int?,
|
||||||
|
val isGameMaster: Boolean?,
|
||||||
|
val isAdmin: Boolean?,
|
||||||
|
) : SettingsJson
|
|
@ -2,6 +2,7 @@ package com.pixelized.desktop.lwa.ui.screen.campaign
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -338,26 +339,25 @@ private fun CampaignLayout(
|
||||||
) {
|
) {
|
||||||
main()
|
main()
|
||||||
}
|
}
|
||||||
Box(
|
Row {
|
||||||
modifier = Modifier
|
Box(
|
||||||
.align(alignment = Alignment.BottomEnd)
|
modifier = Modifier.onSizeChanged { leftPanelState.value = it.toDp(density) },
|
||||||
.onSizeChanged { chatOverlayState.value = it.toDp(density) },
|
) {
|
||||||
) {
|
leftPanel()
|
||||||
chat()
|
}
|
||||||
}
|
Box(
|
||||||
Box(
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.align(alignment = Alignment.Bottom)
|
||||||
.align(alignment = Alignment.CenterStart)
|
.weight(weight = 1f)
|
||||||
.onSizeChanged { leftPanelState.value = it.toDp(density) },
|
.onSizeChanged { chatOverlayState.value = it.toDp(density) },
|
||||||
) {
|
) {
|
||||||
leftPanel()
|
chat()
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier.onSizeChanged { rightPanelState.value = it.toDp(density) },
|
||||||
.align(alignment = Alignment.CenterEnd)
|
) {
|
||||||
.onSizeChanged { rightPanelState.value = it.toDp(density) },
|
rightPanel()
|
||||||
) {
|
}
|
||||||
rightPanel()
|
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortrait
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRoll
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlteration
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player.PlayerRibbonViewModel
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CharacterRibbon(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
layoutDirection: LayoutDirection,
|
||||||
|
viewModel: PlayerRibbonViewModel = koinViewModel(),
|
||||||
|
padding: PaddingValues = PaddingValues(all = 8.dp),
|
||||||
|
onCharacterLeftClick: (characterSheetId: String) -> Unit,
|
||||||
|
onCharacterRightClick: (characterSheetId: String) -> Unit,
|
||||||
|
onLevelUp: (characterSheetId: String) -> Unit,
|
||||||
|
) {
|
||||||
|
val characters = viewModel.characters.collectAsState()
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalLayoutDirection provides layoutDirection
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = modifier,
|
||||||
|
contentPadding = padding,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = characters.value,
|
||||||
|
key = { it.characterSheetId },
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.animateItem()
|
||||||
|
.graphicsLayer { if (it.hideOverruled) this.alpha = 0.3f },
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||||
|
) {
|
||||||
|
Box {
|
||||||
|
CharacterRibbonPortrait(
|
||||||
|
character = it.portrait,
|
||||||
|
onCharacterLeftClick = { onCharacterLeftClick(it.characterSheetId) },
|
||||||
|
onCharacterRightClick = { onCharacterRightClick(it.characterSheetId) },
|
||||||
|
onLevelUp = { onLevelUp(it.characterSheetId) },
|
||||||
|
)
|
||||||
|
CharacterRibbonRoll(
|
||||||
|
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CharacterRibbonAlteration(
|
||||||
|
status = it.status,
|
||||||
|
direction = LayoutDirection.Ltr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,28 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.text
|
package com.pixelized.desktop.lwa.ui.screen.campaign.text
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.expandIn
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.shrinkOut
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
@ -21,13 +31,10 @@ import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.pointer.PointerEventType
|
|
||||||
import androidx.compose.ui.input.pointer.onPointerEvent
|
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.max
|
import androidx.compose.ui.unit.max
|
||||||
|
@ -47,21 +54,21 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.RollTextMessag
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.TextMessage
|
import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.TextMessage
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
|
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
|
||||||
import kotlinx.coroutines.launch
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_more_down_24dp
|
||||||
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class ChatSettingsUio(
|
data class ChatSettingsUio(
|
||||||
val autoShowChat: Boolean,
|
val show: Boolean,
|
||||||
val autoScrollChat: Boolean,
|
val autoScroll: Boolean,
|
||||||
val autoHideChat: Boolean,
|
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun default() = with(SettingsUseCase().defaultSettings()) {
|
fun default() = with(SettingsUseCase().defaultSettings()) {
|
||||||
ChatSettingsUio(
|
ChatSettingsUio(
|
||||||
autoShowChat = chat.autoShowChat,
|
show = chat.showChat,
|
||||||
autoScrollChat = chat.autoScrollChat,
|
autoScroll = chat.autoScrollChat,
|
||||||
autoHideChat = chat.autoHideChat,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,9 +80,9 @@ fun CampaignChat(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
chatViewModel: CampaignChatViewModel = koinViewModel(),
|
chatViewModel: CampaignChatViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val lazyState = rememberLazyListState()
|
val lazyState = rememberLazyListState()
|
||||||
val animatedChatWidth = rememberAnimatedChatWidth()
|
val campaignLayoutScope = LocalCampaignLayoutScope.current
|
||||||
val colorScheme = MaterialTheme.lwa.colorScheme
|
val colorScheme = MaterialTheme.lwa.colorScheme
|
||||||
val messages = chatViewModel.messages.collectAsState()
|
val messages = chatViewModel.messages.collectAsState()
|
||||||
val settings = chatViewModel.settings.collectAsState()
|
val settings = chatViewModel.settings.collectAsState()
|
||||||
|
@ -84,54 +91,63 @@ fun CampaignChat(
|
||||||
lazyState = lazyState,
|
lazyState = lazyState,
|
||||||
messages = messages,
|
messages = messages,
|
||||||
settings = settings,
|
settings = settings,
|
||||||
displayChat = chatViewModel::displayChat,
|
|
||||||
hideChat = chatViewModel::hideChat,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Box(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier.background(
|
||||||
.size(
|
shape = remember { RoundedCornerShape(8.dp) },
|
||||||
width = animatedChatWidth.value,
|
color = remember { colorScheme.elevated.base1dp.copy(alpha = 0.5f) },
|
||||||
height = MaterialTheme.lwa.size.portrait.minimized.height * 2 + 8.dp,
|
),
|
||||||
)
|
|
||||||
.graphicsLayer {
|
|
||||||
alpha = chatViewModel.chatAnimatedVisibility.value
|
|
||||||
}
|
|
||||||
.background(
|
|
||||||
shape = remember { RoundedCornerShape(8.dp) },
|
|
||||||
color = remember { colorScheme.elevated.base1dp.copy(alpha = 0.5f) },
|
|
||||||
)
|
|
||||||
.onPointerEvent(eventType = PointerEventType.Enter) {
|
|
||||||
scope.launch { chatViewModel.displayChat() }
|
|
||||||
}
|
|
||||||
.onPointerEvent(eventType = PointerEventType.Exit) {
|
|
||||||
if (settings.value.autoHideChat) {
|
|
||||||
scope.launch { chatViewModel.hideChat() }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
AnimatedVisibility(
|
||||||
modifier = Modifier.fillMaxSize(),
|
visible = settings.value.show,
|
||||||
state = lazyState,
|
enter = fadeIn() + expandIn(),
|
||||||
verticalArrangement = Arrangement.spacedBy(
|
exit = fadeOut() + shrinkOut(),
|
||||||
space = 4.dp,
|
|
||||||
alignment = Alignment.Bottom,
|
|
||||||
),
|
|
||||||
contentPadding = remember { PaddingValues(all = 8.dp) },
|
|
||||||
) {
|
) {
|
||||||
items(
|
LazyColumn(
|
||||||
items = messages.value,
|
modifier = Modifier
|
||||||
key = { it.id },
|
.width(width = campaignLayoutScope.chatOverlay.value.width - (32.dp + 8.dp))
|
||||||
contentType = { it.javaClass.simpleName }
|
.heightIn(min = MaterialTheme.lwa.size.portrait.minimized.height * 2 + 8.dp),
|
||||||
|
state = lazyState,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(
|
||||||
|
space = 4.dp,
|
||||||
|
alignment = Alignment.Bottom,
|
||||||
|
),
|
||||||
|
contentPadding = remember { PaddingValues(all = 8.dp) },
|
||||||
) {
|
) {
|
||||||
when (it) {
|
items(
|
||||||
is RollTextMessageUio -> RollTextMessage(message = it)
|
items = messages.value,
|
||||||
is PurseTextMessageUio -> PurseTextMessage(message = it)
|
key = { it.id },
|
||||||
is DiminishedTextMessageUio -> DiminishedTextMessage(message = it)
|
contentType = { it.javaClass.simpleName }
|
||||||
is CharacteristicTextMessageUio -> CharacteristicTextMessage(message = it)
|
) {
|
||||||
|
when (it) {
|
||||||
|
is RollTextMessageUio -> RollTextMessage(message = it)
|
||||||
|
is PurseTextMessageUio -> PurseTextMessage(message = it)
|
||||||
|
is DiminishedTextMessageUio -> DiminishedTextMessage(message = it)
|
||||||
|
is CharacteristicTextMessageUio -> CharacteristicTextMessage(message = it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Column {
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier.size(size = 32.dp),
|
||||||
|
onClick = chatViewModel::toggleChat
|
||||||
|
) {
|
||||||
|
val rotation = animateFloatAsState(
|
||||||
|
targetValue = if (settings.value.show) 0f else 180f,
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(size = 16.dp)
|
||||||
|
.graphicsLayer {
|
||||||
|
this.rotationZ = rotation.value
|
||||||
|
},
|
||||||
|
painter = painterResource(Res.drawable.ic_more_down_24dp),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,24 +156,16 @@ private fun ChatScrollDownEffect(
|
||||||
lazyState: LazyListState,
|
lazyState: LazyListState,
|
||||||
messages: State<List<TextMessage>>,
|
messages: State<List<TextMessage>>,
|
||||||
settings: State<ChatSettingsUio>,
|
settings: State<ChatSettingsUio>,
|
||||||
displayChat: suspend () -> Unit,
|
|
||||||
hideChat: suspend () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(
|
LaunchedEffect(
|
||||||
key1 = messages.value.lastOrNull()?.id,
|
key1 = messages.value.lastOrNull()?.id,
|
||||||
) {
|
) {
|
||||||
if (messages.value.isNotEmpty()) {
|
if (messages.value.isNotEmpty()) {
|
||||||
if (settings.value.autoShowChat) {
|
if (settings.value.autoScroll) {
|
||||||
displayChat()
|
|
||||||
}
|
|
||||||
if (settings.value.autoScrollChat) {
|
|
||||||
lazyState.animateScrollToItem(
|
lazyState.animateScrollToItem(
|
||||||
index = messages.value.lastIndex + 1,
|
index = messages.value.lastIndex + 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (settings.value.autoHideChat) {
|
|
||||||
hideChat()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,19 +175,26 @@ private fun ChatScrollDownEffect(
|
||||||
private fun rememberAnimatedChatWidth(
|
private fun rememberAnimatedChatWidth(
|
||||||
campaignScreenScope: CampaignLayoutScope = LocalCampaignLayoutScope.current,
|
campaignScreenScope: CampaignLayoutScope = LocalCampaignLayoutScope.current,
|
||||||
windowsState: WindowState = LocalWindowState.current,
|
windowsState: WindowState = LocalWindowState.current,
|
||||||
|
settings: State<ChatSettingsUio>,
|
||||||
): State<Dp> {
|
): State<Dp> {
|
||||||
val chatWidth = remember(windowsState, campaignScreenScope) {
|
val chatWidth = remember(windowsState, campaignScreenScope) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
val minChatWidth = 64.dp * 8
|
if (settings.value.show) {
|
||||||
val maxChatWidth = 64.dp * 12
|
val minChatWidth = 64.dp * 8
|
||||||
val windowWidth = windowsState.size.width
|
val maxChatWidth = 64.dp * 12
|
||||||
if (windowWidth != Dp.Unspecified) {
|
val windowWidth = windowsState.size.width
|
||||||
val width = windowWidth - campaignScreenScope.leftPanel.value.width - 16.dp
|
if (windowWidth != Dp.Unspecified) {
|
||||||
min(max(width, minChatWidth), maxChatWidth)
|
val width = windowWidth - campaignScreenScope.leftPanel.value.width - 16.dp
|
||||||
|
min(max(width, minChatWidth), maxChatWidth)
|
||||||
|
} else {
|
||||||
|
minChatWidth
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
minChatWidth
|
0.dp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return animateDpAsState(targetValue = chatWidth.value)
|
return animateDpAsState(
|
||||||
|
targetValue = chatWidth.value,
|
||||||
|
)
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.text
|
package com.pixelized.desktop.lwa.ui.screen.campaign.text
|
||||||
|
|
||||||
import androidx.compose.animation.core.Animatable
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
|
@ -13,22 +11,20 @@ import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.runningFold
|
import kotlinx.coroutines.flow.runningFold
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class CampaignChatViewModel(
|
class CampaignChatViewModel(
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val campaignRepository: CampaignRepository,
|
campaignRepository: CampaignRepository,
|
||||||
networkRepository: NetworkRepository,
|
networkRepository: NetworkRepository,
|
||||||
textMessageFactory: TextMessageFactory,
|
textMessageFactory: TextMessageFactory,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val chatAnimatedVisibility = Animatable(0f)
|
|
||||||
|
|
||||||
val settings = settingsRepository.settingsFlow().map {
|
val settings = settingsRepository.settingsFlow().map {
|
||||||
ChatSettingsUio(
|
ChatSettingsUio(
|
||||||
autoShowChat = it.chat.autoShowChat,
|
show = it.chat.showChat,
|
||||||
autoScrollChat = it.chat.autoScrollChat,
|
autoScroll = it.chat.autoScrollChat,
|
||||||
autoHideChat = it.chat.autoHideChat,
|
|
||||||
)
|
)
|
||||||
}.stateIn(
|
}.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
|
@ -59,20 +55,14 @@ class CampaignChatViewModel(
|
||||||
initialValue = emptyList(),
|
initialValue = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun displayChat() {
|
fun toggleChat() {
|
||||||
chatAnimatedVisibility.animateTo(
|
viewModelScope.launch {
|
||||||
targetValue = 1f,
|
val settings = settingsRepository.settingsFlow().value
|
||||||
)
|
settingsRepository.update(
|
||||||
}
|
settings = settings.copy(
|
||||||
|
chat = settings.chat.copy(showChat = settings.chat.showChat.not())
|
||||||
suspend fun hideChat() {
|
)
|
||||||
val settings = settingsRepository.settingsFlow().value
|
|
||||||
chatAnimatedVisibility.animateTo(
|
|
||||||
targetValue = 0f,
|
|
||||||
animationSpec = tween(
|
|
||||||
durationMillis = 2000,
|
|
||||||
delayMillis = settings.chat.autoHideDelay * 1000,
|
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,16 +15,8 @@ import lwacharactersheet.composeapp.generated.resources.ic_fan_focus_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_format_list_numbered_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_format_list_numbered_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_ifl_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_ifl_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_timer_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_timer_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_visibility_off_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_delay_description
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_delay_title
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_description
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_title
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_description
|
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_description
|
||||||
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_title
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_description
|
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__line_count_title
|
||||||
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__section
|
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__section
|
||||||
|
@ -92,48 +84,6 @@ class SettingsViewModel(
|
||||||
SettingSectionUio(
|
SettingSectionUio(
|
||||||
title = Res.string.settings__chat_log__section,
|
title = Res.string.settings__chat_log__section,
|
||||||
),
|
),
|
||||||
SettingToggleItemUio(
|
|
||||||
icon = Res.drawable.ic_visibility_24dp,
|
|
||||||
title = Res.string.settings__chat_log__auto_show_title,
|
|
||||||
description = Res.string.settings__chat_log__auto_show_description,
|
|
||||||
checked = booleanStates.autoShowChat,
|
|
||||||
onToggle = {
|
|
||||||
settingsRepository.update(
|
|
||||||
settings = settings.value.copy(
|
|
||||||
chat = settings.value.chat.copy(autoShowChat = it)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SettingToggleItemUio(
|
|
||||||
icon = Res.drawable.ic_visibility_off_24dp,
|
|
||||||
title = Res.string.settings__chat_log__auto_hide_title,
|
|
||||||
description = Res.string.settings__chat_log__auto_hide_description,
|
|
||||||
checked = booleanStates.autoHideChat,
|
|
||||||
onToggle = {
|
|
||||||
settingsRepository.update(
|
|
||||||
settings = settings.value.copy(
|
|
||||||
chat = settings.value.chat.copy(autoHideChat = it)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SettingNumberItemUio(
|
|
||||||
icon = Res.drawable.ic_timer_24dp,
|
|
||||||
title = Res.string.settings__chat_log__auto_hide_delay_title,
|
|
||||||
description = Res.string.settings__chat_log__auto_hide_delay_description,
|
|
||||||
enable = booleanStates.autoHideChat,
|
|
||||||
value = intStates.autoHideDelay,
|
|
||||||
onValueChange = {
|
|
||||||
if (it in 0..999) {
|
|
||||||
settingsRepository.update(
|
|
||||||
settings = settings.value.copy(
|
|
||||||
chat = settings.value.chat.copy(autoHideDelay = it)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
SettingToggleItemUio(
|
SettingToggleItemUio(
|
||||||
icon = Res.drawable.ic_fan_focus_24dp,
|
icon = Res.drawable.ic_fan_focus_24dp,
|
||||||
title = Res.string.settings__chat_log__auto_scroll_title,
|
title = Res.string.settings__chat_log__auto_scroll_title,
|
||||||
|
@ -170,9 +120,6 @@ class SettingsViewModel(
|
||||||
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
|
intStates.dynamicDiceDelay.value = settings.portrait.dynamicDiceDelay
|
||||||
booleanStates.autoShowChat.value = settings.chat.autoShowChat
|
|
||||||
booleanStates.autoHideChat.value = settings.chat.autoHideChat
|
|
||||||
intStates.autoHideDelay.value = settings.chat.autoHideDelay
|
|
||||||
booleanStates.autoScrollChat.value = settings.chat.autoScrollChat
|
booleanStates.autoScrollChat.value = settings.chat.autoScrollChat
|
||||||
intStates.maxLineCount.value = settings.chat.maxLineCount
|
intStates.maxLineCount.value = settings.chat.maxLineCount
|
||||||
}
|
}
|
||||||
|
@ -197,15 +144,6 @@ class SettingsViewModel(
|
||||||
private val HashMap<String, MutableState<Int>>.dynamicDiceDelay
|
private val HashMap<String, MutableState<Int>>.dynamicDiceDelay
|
||||||
get() = getOrPut("DYNAMIC_DICE_DELAY") { mutableStateOf(settings.value.portrait.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) }
|
|
||||||
|
|
||||||
private val HashMap<String, MutableState<Boolean>>.autoHideChat
|
|
||||||
get() = getOrPut("AUTO_HIDE_CHAT") { mutableStateOf(settings.value.chat.autoHideChat) }
|
|
||||||
|
|
||||||
private val HashMap<String, MutableState<Int>>.autoHideDelay
|
|
||||||
get() = getOrPut("AUTO_HIDE_DELAY") { mutableStateOf(settings.value.chat.autoHideDelay) }
|
|
||||||
|
|
||||||
private val HashMap<String, MutableState<Boolean>>.autoScrollChat
|
private val HashMap<String, MutableState<Boolean>>.autoScrollChat
|
||||||
get() = getOrPut("AUTO_SCROLL_CHAT") { mutableStateOf(settings.value.chat.autoScrollChat) }
|
get() = getOrPut("AUTO_SCROLL_CHAT") { mutableStateOf(settings.value.chat.autoScrollChat) }
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,10 @@ class SettingsUseCase {
|
||||||
playerName = "",
|
playerName = "",
|
||||||
portrait = Settings.Portrait(
|
portrait = Settings.Portrait(
|
||||||
dynamicDice = true,
|
dynamicDice = true,
|
||||||
dynamicDiceDelay = 5000,
|
dynamicDiceDelay = 8000,
|
||||||
),
|
),
|
||||||
chat = Settings.Chat(
|
chat = Settings.Chat(
|
||||||
autoHideChat = true,
|
showChat = true,
|
||||||
autoHideDelay = 8,
|
|
||||||
autoShowChat = true,
|
|
||||||
autoScrollChat = true,
|
autoScrollChat = true,
|
||||||
maxLineCount = 200,
|
maxLineCount = 200,
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue