Some small UI adjustment to the level UP screen & RollOverlay.

This commit is contained in:
Thomas Andres Gomez 2025-03-14 15:07:48 +01:00
parent fce085f70d
commit 35396b6069
23 changed files with 530 additions and 166 deletions

View file

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="generic__arrow">&#x25B8;</string>
<string name="error__missing_character_sheet__label">La feuille de personnage est introuvable</string>
<string name="error__default__action">Ok</string>
@ -112,36 +114,20 @@
<string name="character_sheet__delete_dialog__description">Êtes-vous sûr de vouloir supprimer "%1$s" ?</string>
<string name="tooltip__characteristics__characteristics">Les caractéristiques constituent les aptitudes innées dun personnage comme son intelligence, sa force, son charisme, etc. Elles ne sont pas acquises, mais peuvent être parfois augmentées par un entraînement ou une utilisation réussie. Les caractéristiques des humains normaux varient de 2 (niveau extrêmement bas) à 20 (maximum du potentiel humain), avec une moyenne de 10 ou 11. Plus une caractéristique est élevée plus le personnage est puissant dans cette aptitude.\nÀ la création de votre personnage, répartissez les valeurs suivantes dans les différentes caractéristiques : 15, 15, 13, 11, 10, 9 et 7.</string>
<string name="tooltip__characteristics__strength">La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais lefficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.</string>
<string name="tooltip__characteristics__constitution">La Constitution est une mesure de la ténacité et de la résilience du personnage. Elle sert à résister aux maladies. Mais son aspect le plus important réside dans la détermination du nombre de dommages quun personnage peut supporter avant de succomber.</string>
<string name="tooltip__characteristics__height">La Taille est une mesure de la masse du personnage. Elle ne représente pas forcément la taille en centimètres, mais une idée générale de sa masse physique. Un personnage à la TAI élevée peut être très grand et mince, ou petit et massif, ou de taille moyenne en surpoids.</string>
<string name="tooltip__characteristics__intelligence">LIntelligence représente la capacité de discernement du personnage. Elle ne mesure pas nécessairement la somme dinformations mémorisées, mais laptitude au raisonnement, lacuité intellectuelle, la capacité à résoudre des problèmes et lintuition.</string>
<string name="tooltip__characteristics__power">Le Pouvoir est une mesure presque intangible de la force de volonté, du dynamisme intérieur et de lénergie spirituelle. Il représente également le potentiel magique du personnage.</string>
<string name="tooltip__characteristics__dexterity">La Dextérité mesure la coordination œil-main, la vitesse physique et lagilité générale. La DEX détermine à quelle vitesse un personnage peut agir en combat.</string>
<string name="tooltip__characteristics__charisma">Il représente plusieurs aspects allant de la grâce à la beauté en passant par lattraction que le personnage exerce sur les autres. Un personnage avec un CHA élevé se remarque dans une foule en raison dune intangible combinaison de charme et de présence.</string>
<string name="tooltip__characteristics__strength">La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais lefficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.\n\n- Bonus aux dégats\n- Athlétisme\n- Lancer\n- Saisie</string>
<string name="tooltip__characteristics__constitution">La Constitution est une mesure de la ténacité et de la résilience du personnage. Elle sert à résister aux maladies. Mais son aspect le plus important réside dans la détermination du nombre de dommages quun personnage peut supporter avant de succomber.\n\n- Point de vie maximum\n- Athlétisme\n- Acrobatie</string>
<string name="tooltip__characteristics__height">La Taille est une mesure de la masse du personnage. Elle ne représente pas forcément la taille en centimètres, mais une idée générale de sa masse physique. Un personnage à la TAI élevée peut être très grand et mince, ou petit et massif, ou de taille moyenne en surpoids.\n\n- Bonus aux dégats\n- Point de vie maximum\n- Saisie\n- Intimidation\n- Discrétion (impact négatif)</string>
<string name="tooltip__characteristics__intelligence">LIntelligence représente la capacité de discernement du personnage. Elle ne mesure pas nécessairement la somme dinformations mémorisées, mais laptitude au raisonnement, lacuité intellectuelle, la capacité à résoudre des problèmes et lintuition.\n\n- Bonus d'apprentissage\n- Perception\n- Recherche\n- Empathie\n- Baratin\n- Premiers soins</string>
<string name="tooltip__characteristics__power">Le Pouvoir est une mesure presque intangible de la force de volonté, du dynamisme intérieur et de lénergie spirituelle. Il représente également le potentiel magique du personnage.\n\n- Points de pouvoir maximum\n- Intimidation</string>
<string name="tooltip__characteristics__dexterity">La Dextérité mesure la coordination œil-main, la vitesse physique et lagilité générale. La DEX détermine à quelle vitesse un personnage peut agir en combat.\n\n- Initiative\n- Bagarre\n- Esquive\n- Lancer\n- Acrobatie\n- Discrétion\n- Escamotage\n- Premiers soins</string>
<string name="tooltip__characteristics__charisma">Il représente plusieurs aspects allant de la grâce à la beauté en passant par lattraction que le personnage exerce sur les autres. Un personnage avec un CHA élevé se remarque dans une foule en raison dune intangible combinaison de charme et de présence.\n\n- Empathie\n- Persuasion\n- Persuasion\n- Intimidation\n- Baratin\n- Marchandage\n- Discrétion</string>
<string name="tooltip__sub_characteristics__movement">Le Déplacement (DEP) est une valeur de jeu qui détermine la distance que peut parcourir un personnage en un round de combat. Tous les humains ont un DEP de 10. Le DEP a une valeur réelle flexible, mais généralement, chaque point de DEP équivaut à un déplacement dun mètre. En course, un point équivaut à trois mètres.</string>
<string name="tooltip__sub_characteristics__hit_point">Les points de vie (PV) sont égaux à la somme CON+TAI du personnage, divisée par deux (arrondie au supérieur). Ils sont soustraits lorsque le personnage subit des dommages. Quand les points de vie tombent à 0, le personnage sombre dans linconscience. S'il reste inconscient trop longtemps, il meurt. Tous les points de vie régénèrent naturellement après une nuit de repos.</string>
<string name="tooltip__sub_characteristics__power_point">les points de pouvoir sont égaux au POU et sont dépensés pour utiliser la magie ou dautres pouvoirs. Tous les points de pouvoir régénèrent naturellement après une nuit de repos.</string>
<string name="tooltip__sub_characteristics__bonus_damage">Les personnages plus massifs ou plus forts infligent plus de dégâts quand ils frappent leurs ennemis en combat au corps à corps. Le modificateur sapplique aux dégâts infligés par toute attaque portée par les personnages avec des armes de mêlée (BDC). La moitié de ce bonus s'applique aux attaques de lancer (BDD).</string>
<string name="tooltip__sub_characteristics__bonus_damage">Les personnages plus massifs ou plus forts infligent plus de dégâts quand ils frappent leurs ennemis. Le modificateur sapplique aux dégâts infligés par toute attaque portée par les personnages avec des armes de mêlée (BDC). La moitié de ce bonus s'applique aux attaques de lancer (BDD).\n\nBonus aux dégâts : Force + Taille\n- 2 à 12 : -1d6\n- 12 à 17 : -1d4\n- 18 à 22 : 0\n- 23 à 29 : +1d4\n- 30 à 39 : +1d6\n- Au delà : +2d6</string>
<string name="tooltip__sub_characteristics__armor">Une armure protège son porteur des blessures. Lorsquun personnage est touché en combat par une attaque non magique, soustrayez les points darmure aux points de dégâts infligés. Les dommages au-delà de la protection de larmure surpassent celle-ci et sont infligés au personnage, réduisant ses points de vie actuels.</string>
<string name="tooltip__sub_characteristics__learning">Plus un personnage est intelligent, plus il assimile rapidement les connaissances et plus il digère son expérience efficacement. Ce modificateur s'applique au score des compétences nouvellement acquises et aux jets d'expériences. Il est égal à (INT - 10) x 2 avec une valeur minimale de zéro.</string>
<string name="tooltip__sub_characteristics__hp_grow">Plus un personnage est de bonne constitution, plus son corps se renforce rapidement. Ce modificateur indique le nombre de "PV" maximum que le personnage gagne à chaque progression. Il est égal à CON / 3, arrondi à l'inférieur.</string>
<string name="tooltip__skills__combat">Attaque en combat à mains nues. Une attaque réussie inflige 1D3 + BDGT.</string>
<string name="tooltip__skills__dodge">Éviter une attaque, un projectile, etc.</string>
<string name="tooltip__skills__grab">Maitriser/immobiliser un adversaire.</string>
<string name="tooltip__skills__throw">Viser et lancer quelque chose à travers les airs et vers une cible.</string>
<string name="tooltip__skills__athletics">Réaliser un mouvement nécessitant une certaine force (Enfoncer une porte, nager à contre-courant, etc.).</string>
<string name="tooltip__skills__acrobatics">Réaliser un mouvement nécessitant une certaine agilité (Marcher sur une corde, courir sur la glace, etc.).</string>
<string name="tooltip__skills__perception">Aptitude à percevoir via ses sens ou à percevoir un détail difficile à remarquer.</string>
<string name="tooltip__skills__search">Aptitude à trouver des indices et à émettre des suppositions sur la base de ceux-ci.</string>
<string name="tooltip__skills__empathy">Évaluer les pensées et/ou les motivations cachées dun autre personnage en se basant sur des signaux subliminaux.</string>
<string name="tooltip__skills__persuasion">Influencer quelqu'un ou un groupe de personnes avec tact, grâce, ou de bonnes manières.</string>
<string name="tooltip__skills__intimidation">Influencer quelqu'un ou un groupe de personnes par des menaces, des actions hostiles, ou de la violence physique.</string>
<string name="tooltip__skills__spiel">Mentir ou cacher la vérité avec efficacité, soit verbalement, soit par vos actions.</string>
<string name="tooltip__skills__bargain">Négocier les prix.</string>
<string name="tooltip__skills__discretion">Passer inaperçu.</string>
<string name="tooltip__skills__sleight_of_hand">Réaliser une manipulation ou une fourberie, comme prendre quelque chose à quelqu'un ou dissimuler un objet sur vous-même.</string>
<string name="tooltip__skills__aid">Traiter les blessures légères. Une réussite rétablit 1D3 points de vie. Une réussite spéciale restaure 1D3+3 points de vie.</string>
<string name="network__title">Configuration de la table</string>
<string name="network__player_name__label">Nom du joueur</string>
@ -156,6 +142,7 @@
<string name="network__socket__type_server">Serveur</string>
<string name="network__socket__type_client">Client</string>
<string name="network__socket__type_none">Aucun</string>
<string name="network__require__player_name">Un nom de joueur est requis</string>
<string name="network__connect__message">Vous êtes connecté au serveur</string>
<string name="network__disconnect__message">Vous êtes déconnecté du serveur</string>
<string name="network__message__action">Ok</string>
@ -186,7 +173,10 @@
<string name="level_up__title">Montée de niveau</string>
<string name="level_up__action">Level Up !</string>
<string name="level_up__character_level_description">Passage du niveau %1$d au niveau supérieur : %2$d</string>
<string name="level_up__character__label">niv : %1$d&#x25B8;%2$d</string>
<string name="level_up__character_level_description">Passage du niveau %1$d &#x25B8; %2$d</string>
<string name="level_up__skill_level">niv : %1$d -</string>
</resources>

View file

@ -32,9 +32,6 @@ import androidx.compose.ui.unit.min
import androidx.compose.ui.window.ApplicationScope
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.rememberWindowState
import coil3.ImageLoader
import coil3.compose.setSingletonImageLoaderFactory
import coil3.request.crossfade
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
@ -53,17 +50,18 @@ import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheet
import com.pixelized.desktop.lwa.ui.navigation.window.destination.NetworkWindows
import com.pixelized.desktop.lwa.ui.navigation.window.destination.RollHistoryWindow
import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
import com.pixelized.desktop.lwa.ui.overlay.roll.RollHostState
import com.pixelized.desktop.lwa.ui.overlay.roll.RollOverlay
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import com.pixelized.desktop.lwa.ui.overlay.roll.RollHostState
import com.pixelized.desktop.lwa.ui.overlay.roll.RollOverlay
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
import com.pixelized.desktop.lwa.utils.InstallCoil
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
@ -102,7 +100,7 @@ val LocalBlurController = compositionLocalOf<BlurContentController> {
@Composable
@Preview
fun ApplicationScope.App() {
fun ApplicationScope.LwaApplication() {
val maxWindowHeight = rememberMaxWindowHeight()
val snackHostState = remember { SnackbarHostState() }
val errorSnackHostState = remember { SnackbarHostState() }
@ -120,12 +118,13 @@ fun ApplicationScope.App() {
),
)
// Coil configuration
setSingletonImageLoaderFactory { context ->
ImageLoader.Builder(context)
.crossfade(enable = false)
.build()
}
// Coil configuration.
InstallCoil(
crossfade = false,
diskCachePath = { provider ->
provider.imagesStorePath()
}
)
CompositionLocalProvider(
LocalApplicationScope provides this,
@ -150,16 +149,18 @@ fun ApplicationScope.App() {
}
}
@Composable
private fun MainWindowScreen(
campaignViewModel: CampaignViewModel = koinViewModel(),
dataSyncViewModel: DataSyncViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
campaignViewModel: CampaignViewModel = koinViewModel(),
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
rollViewModel: RollHistoryViewModel = koinViewModel(),
) {
LaunchedEffect(Unit) {
networkViewModel.connect()
campaignViewModel.init()
dataSyncViewModel.autoConnect()
dataSyncViewModel.synchronise()
}
val snackHostState = LocalSnackHost.current
@ -205,15 +206,12 @@ private fun MainWindowScreen(
content = {
BlurContent(
modifier = Modifier.fillMaxSize(),
controller = blurController
controller = blurController,
) {
MainNavHost(
campaignViewModel = campaignViewModel,
networkViewModel = networkViewModel,
campaignChatViewModel = campaignChatViewModel,
)
MainNavHost()
}
RollOverlay(
modifier = Modifier.fillMaxSize(),
hostState = rollHostState,
)
}

View file

@ -0,0 +1,58 @@
package com.pixelized.desktop.lwa
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.repository.settings.SettingsRepository
import com.pixelized.shared.lwa.model.campaign.Campaign
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
class DataSyncViewModel(
private val characterRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository,
private val campaignRepository: CampaignRepository,
private val settingsRepository: SettingsRepository,
private val networkRepository: NetworkRepository,
) : ViewModel() {
fun autoConnect() {
val settings = settingsRepository.settings()
if (settings.playerName.isEmpty()) return
networkRepository.connect(
host = settings.host,
port = settings.port,
)
}
suspend fun synchronise() = coroutineScope {
networkRepository.status
.filter { status -> status == NetworkRepository.Status.CONNECTED }
.onEach { campaignRepository.update() }
.launchIn(this)
networkRepository.status
.filter { status -> status == NetworkRepository.Status.CONNECTED }
.combine(campaignRepository.campaignFlow) { _, campaign: Campaign -> campaign }
.onEach { campaign ->
campaign.characters.keys.forEach { id ->
characterRepository.characterDetail(
characterSheetId = id.characterSheetId,
forceUpdate = true,
)
alterationRepository.updateActiveAlterations(
characterInstanceId = id,
)
}
}
.launchIn(this)
}
}

View file

@ -15,8 +15,8 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsStore
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogFactory
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.levelup.LevelUpViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.TextMessageFactory
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailFactory
@ -30,10 +30,10 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEdi
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEditViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.ui.screen.levelup.LevelUpFactory
import com.pixelized.desktop.lwa.ui.screen.levelup.LevelUpViewModel
import com.pixelized.desktop.lwa.ui.screen.main.MainPageViewModel
import com.pixelized.desktop.lwa.ui.screen.network.NetworkFactory
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.ui.screen.settings.SettingsViewModel
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
@ -116,6 +116,7 @@ val factoryDependencies
val viewModelDependencies
get() = module {
viewModelOf(::DataSyncViewModel)
viewModelOf(::CampaignViewModel)
viewModelOf(::MainPageViewModel)
viewModelOf(::CharacterSheetViewModel)

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
@ -40,7 +41,12 @@ fun TooltipLayout(
else -> TooltipArea(
modifier = modifier,
tooltip = { Tooltip(tooltip = tooltip) },
tooltip = {
Tooltip(
modifier = Modifier.width(width = 448.dp),
tooltip = tooltip,
)
},
content = content,
delayMillis = delayMillis,
tooltipPlacement = tooltipPlacement,

View file

@ -23,9 +23,6 @@ val LocalScreenController = compositionLocalOf<NavHostController> {
@Composable
fun MainNavHost(
controller: NavHostController = rememberNavController(),
campaignViewModel: CampaignViewModel,
networkViewModel: NetworkViewModel,
campaignChatViewModel: CampaignChatViewModel,
startDestination: String = MainDestination.navigationRoute(),
) {
CompositionLocalProvider(
@ -35,11 +32,7 @@ fun MainNavHost(
navController = controller,
startDestination = startDestination,
) {
composableMainPage(
campaignViewModel = campaignViewModel,
networkViewModel = networkViewModel,
campaignChatViewModel = campaignChatViewModel,
)
composableMainPage()
composableSettingsPage()
composableLevelUp()

View file

@ -3,10 +3,7 @@ package com.pixelized.desktop.lwa.ui.navigation.screen.destination
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignScreen
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
object MainDestination {
private const val ROUTE = "main"
@ -15,19 +12,11 @@ object MainDestination {
fun navigationRoute() = ROUTE
}
fun NavGraphBuilder.composableMainPage(
campaignViewModel: CampaignViewModel,
networkViewModel: NetworkViewModel,
campaignChatViewModel: CampaignChatViewModel,
) {
fun NavGraphBuilder.composableMainPage() {
composable(
route = MainDestination.baseRoute(),
) {
CampaignScreen(
campaignViewModel = campaignViewModel,
networkViewModel = networkViewModel,
campaignChatViewModel = campaignChatViewModel,
)
CampaignScreen()
}
}

View file

@ -25,6 +25,7 @@ import org.koin.compose.viewmodel.koinViewModel
@Composable
fun RollOverlay(
viewModel: RollViewModel = koinViewModel(),
modifier: Modifier = Modifier,
hostState: RollHostState,
) {
val blur = LocalBlurController.current
@ -43,7 +44,7 @@ fun RollOverlay(
}
AnimatedContent(
modifier = Modifier.fillMaxSize(),
modifier = modifier,
targetState = hostState.rollAction.value,
transitionSpec = {
val enter = fadeIn() + slideInVertically { 64 }

View file

@ -61,13 +61,11 @@ fun CampaignScreen(
campaignViewModel: CampaignViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
rollViewModel: RollViewModel = koinViewModel(),
) {
val screen = LocalScreenController.current
val blurController = rememberBlurContentController()
val scope = rememberCoroutineScope()
KeyHandler {
when {
it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {

View file

@ -80,7 +80,7 @@ data class CharacterDetailHeaderUio(
@Composable
fun CharacterDetailHeader(
modifier: Modifier = Modifier,
iconSize: Dp = 14.dp,
iconSize: Dp = MaterialTheme.lwa.size.sheet.subCategory,
header: State<CharacterDetailHeaderUio?>,
onDismissRequest: () -> Unit,
onDiminished: () -> Unit,

View file

@ -52,7 +52,7 @@ fun CharacterDetailSheet(
) {
sheet.value?.characteristics?.forEach {
CharacterDetailSheetCharacteristic(
modifier = Modifier.size(size = MaterialTheme.lwa.size.characteristic),
modifier = Modifier.size(size = MaterialTheme.lwa.size.sheet.characteristic),
characteristic = it,
onClick = { onCharacteristic(it) },
)

View file

@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -55,6 +57,7 @@ import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import coil3.compose.AsyncImage
@ -68,6 +71,8 @@ import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristic
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkill
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristic
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristicUio
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
@ -75,7 +80,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet__skills_
import lwacharactersheet.composeapp.generated.resources.character_sheet__skills__magic_title
import lwacharactersheet.composeapp.generated.resources.character_sheet__skills__special_title
import lwacharactersheet.composeapp.generated.resources.level_up__action
import lwacharactersheet.composeapp.generated.resources.level_up__character_level_description
import lwacharactersheet.composeapp.generated.resources.level_up__character__label
import lwacharactersheet.composeapp.generated.resources.level_up__title
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
@ -86,6 +91,11 @@ data class LevelUpHeaderUio(
val name: String,
val level: Int,
val portrait: String?,
val hp: LevelUpSubCharacteristicUio,
val pp: LevelUpSubCharacteristicUio,
val bonus: LevelUpSubCharacteristicUio,
val grow: LevelUpSubCharacteristicUio,
val learn: LevelUpSubCharacteristicUio,
)
@Stable
@ -217,18 +227,49 @@ private fun LevelUpContent(
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
Text(
style = MaterialTheme.lwa.typography.base.h5,
text = header.value?.name ?: ""
)
Text(
style = MaterialTheme.lwa.typography.base.body1,
text = (header.value?.level ?: 0).let {
stringResource(
Res.string.level_up__character_level_description, it, it + 1
)
},
)
Row(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.h5,
text = header.value?.name ?: "",
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.lwa.typography.base.caption,
text = (header.value?.level ?: 0).let { level ->
stringResource(
resource = Res.string.level_up__character__label,
level, level + 1
)
},
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(space = 16.dp),
) {
LevelUpSubCharacteristic(
characteristic = header.value?.hp,
)
LevelUpSubCharacteristic(
characteristic = header.value?.pp,
)
Spacer(
modifier = Modifier.weight(weight = 1f)
)
LevelUpSubCharacteristic(
characteristic = header.value?.bonus,
)
LevelUpSubCharacteristic(
characteristic = header.value?.grow,
)
LevelUpSubCharacteristic(
characteristic = header.value?.learn,
)
}
}
Column(
modifier = Modifier.verticalScroll(state = scrollState),
@ -251,7 +292,7 @@ private fun LevelUpContent(
key = { it.characteristicId }
) {
LevelUpCharacteristic(
modifier = Modifier.size(size = MaterialTheme.lwa.size.characteristic),
modifier = Modifier.size(size = MaterialTheme.lwa.size.sheet.characteristic),
characteristic = it,
onClick = { onCharacteristic(it) },
)

View file

@ -5,10 +5,14 @@ import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSubCharacteristicUio
import com.pixelized.shared.lwa.model.AlteredCharacterSheet
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.model.alteration.Alteration
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.parser.expression.Expression
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__cha
@ -18,6 +22,20 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet__charact
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__int
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__pow
import lwacharactersheet.composeapp.generated.resources.character_sheet__characteristics__str
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__armor
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__damage_bonus
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__hit_point
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__hp_grow
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__learning
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__movement
import lwacharactersheet.composeapp.generated.resources.character_sheet__sub_characteristics__power_point
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_swords_24dp
import lwacharactersheet.composeapp.generated.resources.ic_water_drop_24dp
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__charisma
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__constitution
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__dexterity
@ -25,6 +43,13 @@ import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__intelligence
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__power
import lwacharactersheet.composeapp.generated.resources.tooltip__characteristics__strength
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__armor
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__bonus_damage
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__hit_point
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__hp_grow
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__learning
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__movement
import lwacharactersheet.composeapp.generated.resources.tooltip__sub_characteristics__power_point
import org.jetbrains.compose.resources.getString
class LevelUpFactory(
@ -32,14 +57,96 @@ class LevelUpFactory(
private val expressionUseCase: ExpressionUseCase,
) {
fun convertToLevelUpHeaderUio(
suspend fun convertToLevelUpHeaderUio(
characterSheet: CharacterSheet?,
selectedCharacteristicId: String?,
): LevelUpHeaderUio? {
if (characterSheet == null) return null
val levelAlteration = FieldAlteration(
alterationId = CharacterSheet.CharacteristicId.LVL,
metadata = Alteration.MetaData(
name = "LevelUp-${CharacterSheet.CharacteristicId.LVL}",
description = "Fake alteration for a levelUp simulation. Alter the ${CharacterSheet.CharacteristicId.LVL} stat."
),
expression = Expression.Flat(1),
)
val alteredCharacterSheet = alteredCharacterSheetFactory.sheet(
characterSheet = characterSheet,
alterations = emptyMap(),
)
val levelUpCharacterSheet = alteredCharacterSheetFactory.sheet(
characterSheet = characterSheet,
alterations = selectedCharacteristicId
?.let {
val characteristic = FieldAlteration(
alterationId = it,
metadata = Alteration.MetaData(
name = "LevelUp-$it",
description = "Fake alteration for a levelUp simulation. Alter the $it stat."
),
expression = Expression.Flat(1),
)
mapOf(
CharacterSheet.CharacteristicId.LVL to listOf(levelAlteration),
it to listOf(characteristic),
)
}
?: mapOf(
CharacterSheet.CharacteristicId.LVL to listOf(levelAlteration),
),
)
return LevelUpHeaderUio(
name = characterSheet.name,
portrait = characterSheet.portrait,
level = characterSheet.level,
hp = LevelUpSubCharacteristicUio(
icon = Res.drawable.ic_heart_24dp,
base = "${alteredCharacterSheet.maxHp}",
levelUp = "${levelUpCharacterSheet.maxHp}",
tooltip = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__hit_point),
description = getString(Res.string.tooltip__sub_characteristics__hit_point)
),
),
pp = LevelUpSubCharacteristicUio(
icon = Res.drawable.ic_water_drop_24dp,
base = "${alteredCharacterSheet.maxPp}",
levelUp = "${levelUpCharacterSheet.maxPp}",
tooltip = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__power_point),
description = getString(Res.string.tooltip__sub_characteristics__power_point)
)
),
bonus = LevelUpSubCharacteristicUio(
icon = Res.drawable.ic_swords_24dp,
base = alteredCharacterSheet.damageBonus,
levelUp = levelUpCharacterSheet.damageBonus,
tooltip = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__damage_bonus),
description = getString(Res.string.tooltip__sub_characteristics__bonus_damage)
),
),
grow = LevelUpSubCharacteristicUio(
icon = Res.drawable.ic_heart_plus_24dp,
base = "${alteredCharacterSheet.hpGrow}",
levelUp = "${levelUpCharacterSheet.hpGrow}",
tooltip = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__hp_grow),
description = getString(Res.string.tooltip__sub_characteristics__hp_grow)
),
),
learn = LevelUpSubCharacteristicUio(
icon = Res.drawable.ic_cognition_24dp,
base = "${alteredCharacterSheet.learning}",
levelUp = "${levelUpCharacterSheet.learning}",
tooltip = TooltipUio(
title = getString(Res.string.character_sheet__sub_characteristics__learning),
description = getString(Res.string.tooltip__sub_characteristics__learning)
),
)
)
}
@ -204,6 +311,12 @@ class LevelUpFactory(
level = skill.level,
levelUp = results.isSkillLeveledUp(skillId = skill.id),
occupation = skill.occupation,
tooltips = skill.description?.let {
TooltipUio(
title = skill.label,
description = it,
)
},
roll = when (results[skill.id]) {
null -> RollActionUio(
characterInstanceId = characterInstanceId,

View file

@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.error__default__action
@ -35,14 +34,15 @@ class LevelUpViewModel(
private val results = MutableStateFlow<Map<String, RollResult>>(emptyMap())
private val selectedCharacteristicId = MutableStateFlow<String?>(null)
val header: StateFlow<LevelUpHeaderUio?> = characterSheetRepository
.characterDetailFlow(characterSheetId = argument.characterInstanceId.characterSheetId)
.map(levelUpFactory::convertToLevelUpHeaderUio)
.stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = null,
)
val header = combine(
characterSheetRepository.characterDetailFlow(characterSheetId = argument.characterInstanceId.characterSheetId),
selectedCharacteristicId,
levelUpFactory::convertToLevelUpHeaderUio
).stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = null,
)
val characteristics = combine(
characterSheetRepository.characterDetailFlow(characterSheetId = argument.characterInstanceId.characterSheetId),

View file

@ -64,11 +64,7 @@ fun LevelUpCharacteristic(
.fillMaxWidth()
.align(alignment = Alignment.Center),
targetState = characteristic.selected,
transitionSpec = {
val enter = fadeIn() + slideInVertically { -16 }
val exit = fadeOut() + slideOutVertically { 16 }
enter togetherWith exit using SizeTransform(clip = false)
},
transitionSpec = { fadeIn() togetherWith fadeOut() },
) {
Text(
modifier = Modifier.fillMaxWidth(),

View file

@ -8,6 +8,7 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
@ -23,6 +24,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.shapes.MasteryShape
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio
import com.pixelized.desktop.lwa.ui.theme.lwa
import lwacharactersheet.composeapp.generated.resources.Res
@ -37,9 +40,11 @@ data class LevelUpSkillUio(
val level: Int,
val levelUp: Boolean,
val occupation: Boolean,
val tooltips: TooltipUio?,
val roll: RollActionUio?,
)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LevelUpSkill(
modifier: Modifier = Modifier,
@ -53,7 +58,7 @@ fun LevelUpSkill(
else -> MaterialTheme.lwa.colorScheme.base.primary
}
)
Row(
TooltipLayout(
modifier = Modifier
.let {
when (skill.roll) {
@ -63,60 +68,65 @@ fun LevelUpSkill(
}
.padding(paddingValues = paddingValues)
.then(other = modifier),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalAlignment = Alignment.CenterVertically,
tooltip = skill.tooltips,
) {
MasteryShape(
modifier = Modifier.padding(top = 4.dp),
multiplier = if (skill.occupation) 1 else 0,
)
Row(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.alignByBaseline().weight(1f),
style = MaterialTheme.typography.body1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = skill.label
MasteryShape(
modifier = Modifier.padding(top = 4.dp),
multiplier = if (skill.occupation) 1 else 0,
)
AnimatedContent(
targetState = skill.levelUp,
transitionSpec = {
val enter = fadeIn() + slideInVertically { -16 }
val exit = fadeOut() + slideOutVertically { 16 }
enter togetherWith exit using SizeTransform(clip = false)
}
Row(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Row(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
Text(
modifier = Modifier.alignByBaseline().weight(1f),
style = MaterialTheme.typography.body1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = skill.label
)
AnimatedContent(
targetState = skill.levelUp,
transitionSpec = {
val enter = fadeIn() + slideInVertically { -16 }
val exit = fadeOut() + slideOutVertically { 16 }
enter togetherWith exit using SizeTransform(clip = false)
}
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.caption,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(
resource = Res.string.level_up__skill_level,
when (it) {
true -> skill.level + 1
else -> skill.level
Row(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.caption,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = stringResource(
resource = Res.string.level_up__skill_level,
when (it) {
true -> skill.level + 1
else -> skill.level
},
)
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.Bold,
color = when (it) {
true -> MaterialTheme.lwa.colorScheme.base.secondary
else -> valueColor.value
},
text = when (it) {
true -> "${skill.value + 5}"
else -> "${skill.value}"
},
)
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.Bold,
color = when (it) {
true -> MaterialTheme.lwa.colorScheme.base.secondary
else -> valueColor.value
},
text = when (it) {
true -> "${skill.value + 5}"
else -> "${skill.value}"
},
)
}
}
}
}

View file

@ -0,0 +1,99 @@
package com.pixelized.desktop.lwa.ui.screen.levelup.items
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.theme.lwa
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.generic__arrow
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
@Stable
data class LevelUpSubCharacteristicUio(
val icon: DrawableResource,
val base: String,
val levelUp: String,
val tooltip: TooltipUio,
)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LevelUpSubCharacteristic(
modifier: Modifier = Modifier,
characteristic: LevelUpSubCharacteristicUio?,
) {
TooltipLayout(
modifier = modifier,
tooltip = characteristic?.tooltip,
) {
Row(
verticalAlignment = Alignment.Bottom,
) {
if (characteristic?.icon != null) {
Icon(
modifier = Modifier
.padding(bottom = 4.dp, end = 2.dp)
.size(size = MaterialTheme.lwa.size.sheet.subCategory),
painter = painterResource(characteristic.icon),
contentDescription = null
)
} else {
Box(
modifier = Modifier
.padding(bottom = 4.dp, end = 2.dp)
.size(size = MaterialTheme.lwa.size.sheet.subCategory),
)
}
Text(
modifier = Modifier.padding(bottom = 3.dp),
style = MaterialTheme.typography.caption,
fontWeight = FontWeight.Thin,
text = characteristic?.base ?: "",
)
Text(
modifier = Modifier.padding(bottom = 3.dp),
style = MaterialTheme.typography.caption,
fontWeight = FontWeight.Thin,
text = stringResource(Res.string.generic__arrow),
)
AnimatedContent(
targetState = characteristic?.levelUp ?: "",
transitionSpec = {
val enter = fadeIn() + slideInVertically { -16 }
val exit = fadeOut() + slideOutVertically { 16 }
enter togetherWith exit using SizeTransform(clip = false)
},
) {
Text(
style = MaterialTheme.typography.h6,
color = MaterialTheme.lwa.colorScheme.base.secondary,
fontWeight = FontWeight.Bold,
text = it,
)
}
}
}
}

View file

@ -41,6 +41,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
@ -50,6 +51,7 @@ import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnack
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
import lwacharactersheet.composeapp.generated.resources.network__host__label
@ -103,6 +105,7 @@ fun NetworkScreen(
) {
val screen = LocalScreenController.current
val snack = LocalSnackHost.current
val scope = rememberCoroutineScope()
Surface(
modifier = Modifier.fillMaxSize(),
@ -148,7 +151,7 @@ fun NetworkScreen(
onResetPortChange = viewModel::onResetPortChange,
onPortChange = viewModel::onPortChange,
onResetHostChange = viewModel::onResetHostChange,
onConnect = viewModel::connect,
onConnect = { scope.launch { viewModel.connect() } },
onDisconnect = viewModel::disconnect,
)
}
@ -188,6 +191,7 @@ fun NetworkPage(
viewModel: NetworkViewModel = koinViewModel(),
) {
val snack = LocalSnackHost.current
val scope = rememberCoroutineScope()
Box(
modifier = modifier,
@ -205,7 +209,7 @@ fun NetworkPage(
onResetHostChange = viewModel::onResetHostChange,
onPortChange = viewModel::onPortChange,
onResetPortChange = viewModel::onResetPortChange,
onConnect = viewModel::connect,
onConnect = { scope.launch { viewModel.connect() } },
onDisconnect = viewModel::disconnect,
)
}

View file

@ -1,5 +1,6 @@
package com.pixelized.desktop.lwa.ui.screen.network
import androidx.compose.material.SnackbarDuration
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
@ -9,7 +10,6 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
@ -20,6 +20,10 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.network__message__action
import lwacharactersheet.composeapp.generated.resources.network__require__player_name
import org.jetbrains.compose.resources.getString
class NetworkViewModel(
private val settingsRepository: SettingsRepository,
@ -64,11 +68,13 @@ class NetworkViewModel(
)
init {
settingsRepository.settingsFlow().onEach {
nameFlow.value = it.playerName
hostFlow.value = it.host
portFlow.value = it.port
}.launchIn(viewModelScope)
settingsRepository.settingsFlow()
.onEach {
nameFlow.value = it.playerName
hostFlow.value = it.host
portFlow.value = it.port
}
.launchIn(viewModelScope)
}
fun onPlayerNameChange(player: String) {
@ -91,7 +97,7 @@ class NetworkViewModel(
hostFlow.value = settings.host
}
fun connect() {
suspend fun connect() {
blurController.show()
_isLoading.value = true
@ -109,6 +115,19 @@ class NetworkViewModel(
)
}
if (nameFlow.value.isBlank()) {
_networkError.emit(
ErrorSnackUio(
message = getString(Res.string.network__require__player_name),
action = getString(Res.string.network__message__action),
duration = SnackbarDuration.Short,
)
)
blurController.hide()
_isLoading.value = false
return
}
networkRepository.connect(
host = hostFlow.value,
port = portFlow.value,

View file

@ -3,20 +3,30 @@ package com.pixelized.desktop.lwa.ui.theme.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
@Stable
data class LwaSize(
val characteristic: DpSize,
)
val sheet: Sheet,
) {
@Stable
data class Sheet(
val subCategory: Dp,
val characteristic: DpSize,
)
}
@Composable
@Stable
fun lwaSize(
characteristic: DpSize = DpSize(width = 76.dp, height = 110.dp),
sheet: LwaSize.Sheet = LwaSize.Sheet(
subCategory = 14.dp,
characteristic = DpSize(width = 76.dp, height = 110.dp),
),
) = remember {
LwaSize(
characteristic = characteristic,
sheet = sheet,
)
}

View file

@ -0,0 +1,28 @@
package com.pixelized.desktop.lwa.utils
import androidx.compose.runtime.Composable
import coil3.ImageLoader
import coil3.compose.setSingletonImageLoaderFactory
import coil3.disk.DiskCache
import coil3.request.crossfade
import com.pixelized.shared.lwa.utils.PathProvider
import okio.Path.Companion.toPath
import org.koin.compose.koinInject
@Composable
fun InstallCoil(
pathProvider: PathProvider = koinInject(),
crossfade: Boolean,
diskCachePath: (PathProvider) -> String,
) {
setSingletonImageLoaderFactory { context ->
ImageLoader.Builder(context)
.crossfade(enable = crossfade)
.diskCache {
DiskCache.Builder()
.directory(directory = diskCachePath(pathProvider).toPath())
.build()
}
.build()
}
}

View file

@ -13,7 +13,7 @@ fun main() {
}
application {
KoinContext {
App()
LwaApplication()
}
}
}

View file

@ -14,6 +14,16 @@ class PathProvider(
}
}
fun imagesStorePath(
os: OperatingSystem = this.operatingSystem,
app: String = this.appName,
): String {
return when (os) {
OperatingSystem.Windows -> "${storePath(os = os, app = app)}_images\\"
OperatingSystem.Macintosh -> "${storePath(os = os, app = app)}_images/"
}
}
fun characterStorePath(
os: OperatingSystem = this.operatingSystem,
app: String = this.appName,