diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_account_child_invert_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_account_child_invert_24dp.xml new file mode 100644 index 0000000..00a4367 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_account_child_invert_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_blur_on_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_blur_on_24dp.xml new file mode 100644 index 0000000..939aff7 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_blur_on_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_iron_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_iron_24dp.xml new file mode 100644 index 0000000..a9dc9ed --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_iron_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_special_character_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_special_character_24dp.xml new file mode 100644 index 0000000..465ecfc --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_special_character_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index f577fbb..ee7e5fa 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -241,5 +241,6 @@ Retirer du groupe Ajouter aux Npcs Retirer des Npcs + Créer un personnage \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt index 34795a8..b2e5a63 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt @@ -1,9 +1,6 @@ package com.pixelized.desktop.lwa -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold @@ -13,7 +10,6 @@ import androidx.compose.material.SnackbarDefaults import androidx.compose.material.SnackbarDuration import androidx.compose.material.SnackbarHost import androidx.compose.material.SnackbarHostState -import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.runtime.Composable @@ -34,9 +30,8 @@ import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState 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 +import com.pixelized.desktop.lwa.ui.composable.LwaScaffold import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController -import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController import com.pixelized.desktop.lwa.ui.composable.key.KeyEventHandler import com.pixelized.desktop.lwa.ui.composable.key.LocalKeyEventHandlers import com.pixelized.desktop.lwa.ui.navigation.screen.MainNavHost @@ -51,7 +46,6 @@ import com.pixelized.desktop.lwa.ui.navigation.window.destination.GameMasterWind 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.characterSheet.CharacterSheetMainNavHost import com.pixelized.desktop.lwa.ui.screen.gamemaster.GameMasterScreen import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage @@ -97,12 +91,8 @@ val LocalBlurController = compositionLocalOf { @Preview fun ApplicationScope.LwaApplication() { val maxWindowHeight = rememberMaxWindowHeight() - val snackHostState = remember { SnackbarHostState() } - val errorSnackHostState = remember { SnackbarHostState() } val windowController = remember { WindowController(maxWindowHeight) } val keyEventHandlers = remember { mutableStateListOf() } - val rollHostState = remember { RollHostState() } - val blurController = rememberBlurContentController() val windowsState = rememberWindowState( size = DpSize( width = 800.dp, @@ -121,30 +111,31 @@ fun ApplicationScope.LwaApplication() { } ) - CompositionLocalProvider( - LocalApplicationScope provides this, - LocalSnackHost provides snackHostState, - LocalErrorSnackHost provides errorSnackHostState, - LocalWindowController provides windowController, - LocalKeyEventHandlers provides keyEventHandlers, - LocalRollHostState provides rollHostState, - LocalBlurController provides blurController, - LocalWindowState provides windowsState, - ) { - Window( - onCloseRequest = ::exitApplication, - state = windowsState, - title = runBlocking { getString(Res.string.app_name) }, - onKeyEvent = { event -> - keyEventHandlers.reversed().any { it(event) } - }, + LwaTheme { + CompositionLocalProvider( + LocalApplicationScope provides this, + LocalWindowController provides windowController, + LocalKeyEventHandlers provides keyEventHandlers, + LocalWindowState provides windowsState, ) { - MainWindowScreen() + Window( + onCloseRequest = ::exitApplication, + state = windowsState, + title = runBlocking { getString(Res.string.app_name) }, + onKeyEvent = { event -> + keyEventHandlers.reversed().any { it(event) } + }, + ) { + MainWindowScreen() + } + + WindowsHandler( + windowController = windowController, + ) } } } - @Composable private fun MainWindowScreen( dataSyncViewModel: DataSyncViewModel = koinViewModel(), @@ -154,66 +145,14 @@ private fun MainWindowScreen( dataSyncViewModel.synchronise() } - val snackHostState = LocalSnackHost.current - val errorSnackHostState = LocalErrorSnackHost.current - val windowController = LocalWindowController.current - val rollHostState = LocalRollHostState.current - val blurController = LocalBlurController.current + LwaScaffold( + modifier = Modifier.fillMaxSize(), + ) { + MainNavHost() - LwaTheme { - Surface( - modifier = Modifier.fillMaxSize() - ) { - Scaffold( - snackbarHost = { - Column( - modifier = Modifier.padding(all = 8.dp), - verticalArrangement = Arrangement.spacedBy(space = 4.dp) - ) { - SnackbarHost( - hostState = snackHostState, - snackbar = { - Snackbar( - snackbarData = it, - backgroundColor = MaterialTheme.colors.surface, - contentColor = MaterialTheme.colors.onSurface, - actionColor = MaterialTheme.colors.onSurface, - ) - } - ) - SnackbarHost( - hostState = errorSnackHostState, - snackbar = { - Snackbar( - snackbarData = it, - backgroundColor = MaterialTheme.colors.error, - contentColor = MaterialTheme.colors.onError, - actionColor = MaterialTheme.colors.onError, - ) - } - ) - } - }, - content = { - BlurContent( - modifier = Modifier.fillMaxSize(), - controller = blurController, - ) { - MainNavHost() - } - RollOverlay( - modifier = Modifier.fillMaxSize(), - hostState = rollHostState, - ) - } - ) - NetworkSnackHandler( - snack = snackHostState, - ) - WindowsHandler( - windowController = windowController, - ) - } + NetworkSnackHandler( + snack = LocalSnackHost.current, + ) } } @@ -239,7 +178,9 @@ private fun WindowsHandler( is RollHistoryWindow -> RollHistoryPage() - is GameMasterWindow -> GameMasterScreen() + is GameMasterWindow -> LwaScaffold { + GameMasterScreen() + } } } ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt index 0a9fa8f..0eaeaf0 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt @@ -16,6 +16,7 @@ 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.composable.character.diminished.CharacterSheetDiminishedDialogFactory +import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlayViewModel import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.network.NetworkFactory @@ -23,7 +24,6 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.network.NetworkViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailFactory import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel -import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.npc.NpcRibbonViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player.PlayerRibbonViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.text.CampaignChatViewModel @@ -34,9 +34,10 @@ import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetV import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.CharacterSheetEditFactory 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.gamemaster.GameMasterActionUseCase -import com.pixelized.desktop.lwa.ui.screen.gamemaster.GameMasterFactory import com.pixelized.desktop.lwa.ui.screen.gamemaster.GameMasterViewModel +import com.pixelized.desktop.lwa.ui.screen.gamemaster.action.GMActionViewModel +import com.pixelized.desktop.lwa.ui.screen.gamemaster.character.GMCharacterFactory +import com.pixelized.desktop.lwa.ui.screen.gamemaster.character.GMCharacterViewModel 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.rollhistory.RollHistoryViewModel @@ -120,7 +121,8 @@ val factoryDependencies factoryOf(::CharacterSheetDiminishedDialogFactory) factoryOf(::TextMessageFactory) factoryOf(::LevelUpFactory) - factoryOf(::GameMasterFactory) + factoryOf(::GMCharacterFactory) + factoryOf(::GMActionViewModel) } val viewModelDependencies @@ -140,12 +142,12 @@ val viewModelDependencies viewModelOf(::CampaignChatViewModel) viewModelOf(::SettingsViewModel) viewModelOf(::LevelUpViewModel) - viewModelOf(::GameMasterViewModel) viewModelOf(::PortraitOverlayViewModel) + viewModelOf(::GMCharacterViewModel) + viewModelOf(::GameMasterViewModel) } val useCaseDependencies get() = module { factoryOf(::SettingsUseCase) - factoryOf(::GameMasterActionUseCase) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/LwaScaffold.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/LwaScaffold.kt new file mode 100644 index 0000000..cd43df0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/LwaScaffold.kt @@ -0,0 +1,91 @@ +package com.pixelized.desktop.lwa.ui.composable + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.SnackbarHost +import androidx.compose.material.SnackbarHostState +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.LocalBlurController +import com.pixelized.desktop.lwa.LocalErrorSnackHost +import com.pixelized.desktop.lwa.LocalRollHostState +import com.pixelized.desktop.lwa.LocalSnackHost +import com.pixelized.desktop.lwa.Snackbar +import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent +import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController +import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController +import com.pixelized.desktop.lwa.ui.overlay.roll.RollHostState +import com.pixelized.desktop.lwa.ui.overlay.roll.RollOverlay + +@Composable +fun LwaScaffold( + modifier: Modifier = Modifier, + snackHostState: SnackbarHostState = remember { SnackbarHostState() }, + errorSnackHostState: SnackbarHostState = remember { SnackbarHostState() }, + rollHostState: RollHostState = remember { RollHostState() }, + blurController: BlurContentController = rememberBlurContentController(), + content: @Composable BoxScope.() -> Unit, +) { + CompositionLocalProvider( + LocalSnackHost provides snackHostState, + LocalErrorSnackHost provides errorSnackHostState, + LocalRollHostState provides rollHostState, + LocalBlurController provides blurController, + ) { + Surface( + modifier = modifier, + ) { + Scaffold( + snackbarHost = { + Column( + modifier = Modifier.padding(all = 8.dp), + verticalArrangement = Arrangement.spacedBy(space = 4.dp) + ) { + SnackbarHost( + hostState = snackHostState, + snackbar = { + Snackbar( + snackbarData = it, + backgroundColor = MaterialTheme.colors.surface, + contentColor = MaterialTheme.colors.onSurface, + actionColor = MaterialTheme.colors.onSurface, + ) + } + ) + SnackbarHost( + hostState = errorSnackHostState, + snackbar = { + Snackbar( + snackbarData = it, + backgroundColor = MaterialTheme.colors.error, + contentColor = MaterialTheme.colors.onError, + actionColor = MaterialTheme.colors.onError, + ) + } + ) + } + }, + content = { + BlurContent( + modifier = Modifier.fillMaxSize(), + controller = blurController, + content = content, + ) + RollOverlay( + modifier = Modifier.fillMaxSize(), + hostState = rollHostState, + ) + } + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMActionDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMActionDestination.kt new file mode 100644 index 0000000..183deb0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMActionDestination.kt @@ -0,0 +1,30 @@ +package com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster + +import androidx.compose.runtime.Stable +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.pixelized.desktop.lwa.ui.screen.gamemaster.action.GMActionPage + +@Stable +object GMActionDestination { + private const val ROUTE = "GameMasterAction" + + fun baseRoute() = ROUTE + + @Stable + fun navigationRoute() = ROUTE +} + +fun NavGraphBuilder.composableGameMasterActionPage() { + composable( + route = GMActionDestination.baseRoute(), + ) { + GMActionPage() + } +} + +fun NavHostController.navigateToGameMasterActionPage() { + val route = GMActionDestination.navigationRoute() + navigate(route = route) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationDestination.kt new file mode 100644 index 0000000..efd0e75 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationDestination.kt @@ -0,0 +1,25 @@ +package com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable + +object GMAlterationDestination { + private const val ROUTE = "GameMasterAlteration" + + fun baseRoute() = ROUTE + fun navigationRoute() = ROUTE +} + +fun NavGraphBuilder.composableGameMasterAlterationPage() { + composable( + route = GMAlterationDestination.baseRoute(), + ) { + + } +} + +fun NavHostController.navigateToGameMasterAlterationPage() { + val route = GMAlterationDestination.navigationRoute() + navigate(route = route) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMCharacterDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMCharacterDestination.kt new file mode 100644 index 0000000..a21a801 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMCharacterDestination.kt @@ -0,0 +1,26 @@ +package com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.pixelized.desktop.lwa.ui.screen.gamemaster.character.GMCharacterPage + +object GMCharacterDestination { + private const val ROUTE = "GameMasterCharacter" + + fun baseRoute() = ROUTE + fun navigationRoute() = ROUTE +} + +fun NavGraphBuilder.composableGameMasterCharacterPage() { + composable( + route = GMCharacterDestination.baseRoute(), + ) { + GMCharacterPage() + } +} + +fun NavHostController.navigateToGameMasterCharacterPage() { + val route = GMCharacterDestination.navigationRoute() + navigate(route = route) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMObjectDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMObjectDestination.kt new file mode 100644 index 0000000..43d05b1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMObjectDestination.kt @@ -0,0 +1,25 @@ +package com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable + +object GMObjectDestination { + private const val ROUTE = "GameMasterObject" + + fun baseRoute() = ROUTE + fun navigationRoute() = ROUTE +} + +fun NavGraphBuilder.composableGameMasterObjectPage() { + composable( + route = GMObjectDestination.baseRoute(), + ) { + + } +} + +fun NavHostController.navigateToGameMasterObjectPage() { + val route = GMObjectDestination.navigationRoute() + navigate(route = route) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt index d2e158e..db8625b 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt @@ -21,7 +21,7 @@ fun WindowController.navigateToGameMasterWindow( window = GameMasterWindow( title = title, size = DpSize( - width = 400.dp + 64.dp, + width = 124.dp * 4 + 64.dp, height = maxWindowHeight - 32.dp, ) ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/player/PlayerRibbonViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/player/PlayerRibbonViewModel.kt index 5daa0e8..8eaaec4 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/player/PlayerRibbonViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/player/ribbon/player/PlayerRibbonViewModel.kt @@ -29,7 +29,7 @@ class PlayerRibbonViewModel( campaign: Campaign, settings: Settings, ): Set { - return if (campaign.options.showParty) campaign.characters else emptySet() + return if (campaign.options.showParty || settings.isGameMaster == true) campaign.characters else emptySet() } override fun hideOverruled(campaign: Campaign, settings: Settings): Boolean { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterActionUseCase.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterActionUseCase.kt deleted file mode 100644 index 80639e7..0000000 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterActionUseCase.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.pixelized.desktop.lwa.ui.screen.gamemaster - -import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository -import com.pixelized.desktop.lwa.repository.network.NetworkRepository -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterUio.Action -import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent - -class GameMasterActionUseCase( - private val campaignRepository: CampaignRepository, - private val networkRepository: NetworkRepository, -) { - suspend fun handleAction( - characterSheetId: String, - action: Action, - ) { - when (action) { - Action.DisplayPortrait -> networkRepository.share( - GameMasterEvent.DisplayPortrait( - timestamp = System.currentTimeMillis(), - characterSheetId = characterSheetId, - ) - ) - - Action.AddToGroup -> campaignRepository.addCharacter( - characterSheetId = characterSheetId, - ) - - Action.AddToNpc -> campaignRepository.addNpc( - characterSheetId = characterSheetId, - ) - - is Action.RemoveFromGroup -> campaignRepository.removeCharacter( - characterSheetId = characterSheetId, - ) - - is Action.RemoveFromNpc -> campaignRepository.removeNpc( - characterSheetId = characterSheetId, - ) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterScreen.kt index e2ce2c5..cba245f 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterScreen.kt @@ -1,144 +1,93 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.draggable -import androidx.compose.foundation.gestures.rememberDraggableState -import androidx.compose.foundation.gestures.scrollBy 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.Row +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Surface import androidx.compose.material.Switch import androidx.compose.material.Text import androidx.compose.material.TopAppBar -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.pixelized.desktop.lwa.LocalWindowController -import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField -import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio -import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacter -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterUio -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTag -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagUio +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController +import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.GMActionDestination +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.composableGameMasterActionPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.composableGameMasterAlterationPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.composableGameMasterCharacterPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.composableGameMasterObjectPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.navigateToGameMasterActionPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.navigateToGameMasterAlterationPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.navigateToGameMasterCharacterPage +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.navigateToGameMasterObjectPage +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTab +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTabUio import com.pixelized.desktop.lwa.ui.theme.color.component.LwaSwitchColors import com.pixelized.desktop.lwa.ui.theme.lwa -import kotlinx.coroutines.launch import lwacharactersheet.composeapp.generated.resources.Res -import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__create__title -import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__edit__title import lwacharactersheet.composeapp.generated.resources.game_master__action import lwacharactersheet.composeapp.generated.resources.game_master__title -import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp -import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp -import lwacharactersheet.composeapp.generated.resources.ic_visibility_off_24dp -import org.jetbrains.compose.resources.getString -import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel + @Composable fun GameMasterScreen( viewModel: GameMasterViewModel = koinViewModel(), ) { - val windows = LocalWindowController.current - val scope = rememberCoroutineScope() - - val characters = viewModel.characters.collectAsState() - val gameMaster = viewModel.gameMaster.collectAsState() - val npcVisibility = viewModel.npcVisibility.collectAsState() - val tags = viewModel.tags.collectAsState() + val screen = rememberNavController() + val gameMaster = viewModel.isGameMaster.collectAsState() Surface( modifier = Modifier.fillMaxSize() ) { - GameMasterContent( - modifier = Modifier.fillMaxSize(), - filter = viewModel.filter, - tags = tags, - gameMaster = gameMaster, - npcVisibility = npcVisibility, - characters = characters, - onTag = viewModel::onTag, - onGameMaster = viewModel::onGameMaster, - onCharacterAction = viewModel::onCharacterAction, - onCharacterSheetEdit = { characterSheetId -> - scope.launch { - windows.navigateToCharacterSheetEdit( - characterId = characterSheetId, - title = getString(Res.string.character_sheet_edit__edit__title), - ) - } - }, - onCharacterSheetCreate = { - scope.launch { - windows.navigateToCharacterSheetEdit( - characterId = null, - title = getString(Res.string.character_sheet_edit__create__title), - ) - } - }, - onNpcVisibility = { - scope.launch { - viewModel.onNpcVisibility() - } - }, - ) + CompositionLocalProvider( + LocalScreenController provides screen, + ) { + GameMasterContent( + modifier = Modifier.fillMaxSize(), + controller = screen, + gameMaster = gameMaster, + onGameMaster = viewModel::onGameMaster, + onTab = { + when (it) { + GMTabUio.Actions -> screen.navigateToGameMasterActionPage() + GMTabUio.Characters -> screen.navigateToGameMasterCharacterPage() + GMTabUio.Alterations -> screen.navigateToGameMasterAlterationPage() + GMTabUio.Objects -> screen.navigateToGameMasterObjectPage() + } + }, + ) + } } } @Composable private fun GameMasterContent( modifier: Modifier = Modifier, - padding: Dp = 16.dp, - spacing: Dp = 8.dp, - filterChipsState: LazyListState = rememberLazyListState(), - filter: LwaTextFieldUio, - tags: State>, + controller: NavHostController = rememberNavController(), gameMaster: State, - npcVisibility: State, - characters: State>, onGameMaster: (Boolean) -> Unit, - onTag: (GMTagUio.TagId) -> Unit, - onCharacterAction: (String, GMCharacterUio.Action) -> Unit, - onCharacterSheetEdit: (String) -> Unit, - onCharacterSheetCreate: () -> Unit, - onNpcVisibility: () -> Unit, + onTab: (GMTabUio) -> Unit, ) { - val scope = rememberCoroutineScope() - - GameMasterLayout( + Scaffold( modifier = modifier, topBar = { TopAppBar( @@ -171,160 +120,46 @@ private fun GameMasterContent( } ) }, - content = { - Column { + content = { paddingValues -> + Row( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues), + ) { Surface( elevation = 1.dp, ) { - Column { - LwaTextField( - modifier = Modifier.fillMaxWidth(), - field = filter, - trailingIcon = { - val value = filter.valueFlow.collectAsState() - AnimatedVisibility( - visible = value.value.isNotBlank(), - enter = fadeIn(), - exit = fadeOut(), - ) { - IconButton( - onClick = { filter.onValueChange.invoke("") }, - ) { - Icon( - painter = painterResource(Res.drawable.ic_cancel_24dp), - tint = MaterialTheme.lwa.colorScheme.base.primary, - contentDescription = null, - ) - } - } - } - ) - LazyRow( - modifier = Modifier.draggable( - orientation = Orientation.Horizontal, - state = rememberDraggableState { delta -> - scope.launch { - filterChipsState.scrollBy(-delta) - } - }, - ), - state = filterChipsState, - contentPadding = remember(padding, spacing) { - PaddingValues(horizontal = padding, vertical = spacing) - }, - horizontalArrangement = Arrangement.spacedBy(space = spacing), - ) { - items( - items = tags.value, - ) { tag -> - GMTag( - tag = tag, - onTag = { onTag(tag.id) }, - ) - } - } - } - } - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - ) { - LazyColumn( - modifier = Modifier.matchParentSize(), - contentPadding = remember { - PaddingValues( - start = padding, - top = padding, - end = padding, - bottom = padding + 48.dp + padding, - ) - }, - verticalArrangement = Arrangement.spacedBy(space = spacing), + Column( + modifier = Modifier + .fillMaxHeight() + .width(width = 64.dp) + .padding(vertical = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(space = 8.dp), ) { - items( - items = characters.value, - key = { it.characterSheetId }, - ) { character -> - GMCharacter( - modifier = Modifier - .fillMaxWidth() - .animateItem(), - character = character, - onEdit = { - onCharacterSheetEdit(character.characterSheetId) - }, - onAction = { action -> - onCharacterAction(character.characterSheetId, action) - }, + GMTabUio.entries.forEach { + GMTab( + tab = it, + onClick = { onTab(it) }, ) } } } - } - }, - fab = { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(all = padding), - horizontalArrangement = Arrangement.SpaceBetween, - ) { - IconButton( - modifier = Modifier.background( - color = MaterialTheme.lwa.colorScheme.base.primary, - shape = CircleShape, - ), - onClick = onNpcVisibility, + Surface( + modifier = Modifier + .fillMaxHeight() + .weight(weight = 1f), ) { - Icon( - painter = when (npcVisibility.value) { - true -> painterResource(Res.drawable.ic_visibility_off_24dp) - else -> painterResource(Res.drawable.ic_visibility_24dp) - }, - tint = MaterialTheme.lwa.colorScheme.base.onPrimary, - contentDescription = null, - ) - } - IconButton( - modifier = Modifier.background( - color = MaterialTheme.lwa.colorScheme.base.primary, - shape = CircleShape, - ), - onClick = onCharacterSheetCreate, - ) { - Icon( - imageVector = Icons.Default.Add, - tint = MaterialTheme.lwa.colorScheme.base.onPrimary, - contentDescription = null, - ) - } - } - } - ) -} - -@Composable -private fun GameMasterLayout( - modifier: Modifier, - topBar: @Composable () -> Unit, - content: @Composable () -> Unit, - fab: @Composable () -> Unit, -) { - Scaffold( - modifier = modifier, - topBar = topBar, - content = { paddingValues -> - Box( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues = paddingValues) - ) { - content() - Row( - modifier = Modifier.align(alignment = Alignment.BottomStart), - ) { - fab() + NavHost( + modifier = Modifier.fillMaxSize(), + navController = controller, + startDestination = GMActionDestination.navigationRoute(), + ) { + composableGameMasterActionPage() + composableGameMasterCharacterPage() + composableGameMasterAlterationPage() + composableGameMasterObjectPage() + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterViewModel.kt index 3e8f2ce..756bb34 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterViewModel.kt @@ -2,83 +2,16 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -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.desktop.lwa.ui.composable.textfield.LwaTextFieldUio -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterUio -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagUio -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagUio.TagId -import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import lwacharactersheet.composeapp.generated.resources.Res -import lwacharactersheet.composeapp.generated.resources.game_master__character__filter -import lwacharactersheet.composeapp.generated.resources.game_master__character_tag__character -import lwacharactersheet.composeapp.generated.resources.game_master__character_tag__npc -import org.jetbrains.compose.resources.getString class GameMasterViewModel( - campaignRepository: CampaignRepository, - characterSheetRepository: CharacterSheetRepository, private val settingsRepository: SettingsRepository, - private val networkRepository: NetworkRepository, - private val factory: GameMasterFactory, - private val useCase: GameMasterActionUseCase, -) : ViewModel() { +): ViewModel() { - private val _filter = MutableStateFlow("") - val filter = LwaTextFieldUio( - enable = true, - labelFlow = MutableStateFlow(runBlocking { getString(Res.string.game_master__character__filter) }), - valueFlow = _filter, - isError = MutableStateFlow(false), - placeHolderFlow = MutableStateFlow(null), - onValueChange = { _filter.value = it }, - ) - - private val _tags = MutableStateFlow(mapOf(TagId.PLAYER to false, TagId.NPC to false)) - val tags = _tags.map { it: Map -> - it.map { (tag, highlight) -> - when (tag) { - TagId.PLAYER -> GMTagUio( - id = TagId.PLAYER, - label = getString(Res.string.game_master__character_tag__character), - highlight = highlight, - ) - - TagId.NPC -> GMTagUio( - id = TagId.NPC, - label = getString(Res.string.game_master__character_tag__npc), - highlight = highlight, - ) - } - } - }.stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = emptyList(), - ) - - val characters = combine( - campaignRepository.campaignFlow, - characterSheetRepository.characterSheetPreviewFlow, - filter.valueFlow, - _tags, - factory::convertToGMCharacterPreviewUio, - ).stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = emptyList(), - ) - - val gameMaster = settingsRepository.settingsFlow() + val isGameMaster = settingsRepository.settingsFlow() .map { it.isGameMaster ?: false } .stateIn( scope = viewModelScope, @@ -86,14 +19,6 @@ class GameMasterViewModel( initialValue = false, ) - val npcVisibility = campaignRepository.campaignFlow - .map { it.options.showNpcs } - .stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - fun onGameMaster(value: Boolean) { val settings = settingsRepository.settings() settingsRepository.update( @@ -102,36 +27,4 @@ class GameMasterViewModel( ) ) } - - fun onCharacterAction( - characterSheetId: String, - action: GMCharacterUio.Action, - ) { - viewModelScope.launch { - try { - useCase.handleAction( - characterSheetId = characterSheetId, - action = action, - ) - } catch (exception: Exception) { - // TODO - } - } - } - - fun onTag( - id: TagId, - ) { - _tags.value = _tags.value.toMutableMap().also { - it[id] = it.getOrPut(id) { true }.not() - } - } - - suspend fun onNpcVisibility() { - networkRepository.share( - GameMasterEvent.ToggleNpc( - timestamp = System.currentTimeMillis(), - ) - ) - } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt new file mode 100644 index 0000000..cf3305c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt @@ -0,0 +1,99 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.action + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMAction +import kotlinx.coroutines.launch +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp +import lwacharactersheet.composeapp.generated.resources.ic_visibility_off_24dp +import org.koin.compose.viewmodel.koinViewModel + +@Stable +data class ActionPageUio( + val party: Boolean, + val npc: Boolean, +) + +@Composable +fun GMActionPage( + viewModel: GMActionViewModel = koinViewModel(), +) { + val scope = rememberCoroutineScope() + val scroll = rememberScrollState() + val actions = viewModel.actions.collectAsState() + + GMActionContent( + actions = actions, + scroll = scroll, + onPartyVisibility = { + scope.launch { + viewModel.onPlayerVisibility() + } + }, + onNpcVisibility = { + scope.launch { + viewModel.onNpcVisibility() + } + }, + ) +} + +@Composable +fun GMActionContent( + modifier: Modifier = Modifier, + scroll: ScrollState, + spacing: Dp = 8.dp, + actions: State, + onPartyVisibility: () -> Unit, + onNpcVisibility: () -> Unit, +) { + Column( + modifier = modifier + .verticalScroll(state = scroll) + .padding(vertical = spacing, horizontal = spacing), + verticalArrangement = Arrangement.spacedBy(space = spacing), + ) { + actions.value?.party?.let { party -> + GMAction( + modifier = Modifier.fillMaxWidth(), + icon = when (party) { + true -> Res.drawable.ic_visibility_off_24dp + else -> Res.drawable.ic_visibility_24dp + }, + label = when (party) { + true -> "Cacher les Joueurs" + else -> "Afficher les Joueurs" + }, + onAction = onPartyVisibility, + ) + } + actions.value?.npc?.let { npc -> + GMAction( + modifier = Modifier.fillMaxWidth(), + icon = when (npc) { + true -> Res.drawable.ic_visibility_off_24dp + else -> Res.drawable.ic_visibility_24dp + }, + label = when (npc) { + true -> "Cacher les NPCs" + else -> "Afficher les NPCs" + }, + onAction = onNpcVisibility, + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt new file mode 100644 index 0000000..f858d05 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt @@ -0,0 +1,48 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.action + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository +import com.pixelized.desktop.lwa.repository.network.NetworkRepository +import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class GMActionViewModel( + private val networkRepository: NetworkRepository, + campaignRepository: CampaignRepository, +) : ViewModel() { + + val actions: StateFlow = campaignRepository.campaignFlow + .map { + ActionPageUio( + party = it.options.showParty, + npc = it.options.showNpcs, + ) + } + .distinctUntilChanged() + .stateIn( + scope = viewModelScope, + started = SharingStarted.Lazily, + initialValue = null, + ) + + suspend fun onNpcVisibility() { + networkRepository.share( + GameMasterEvent.ToggleNpc( + timestamp = System.currentTimeMillis(), + ) + ) + } + + suspend fun onPlayerVisibility() { + networkRepository.share( + GameMasterEvent.TogglePlayer( + timestamp = System.currentTimeMillis(), + ) + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterFactory.kt similarity index 80% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterFactory.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterFactory.kt index aeaa6cb..4c8edf4 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/GameMasterFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterFactory.kt @@ -1,8 +1,8 @@ -package com.pixelized.desktop.lwa.ui.screen.gamemaster +package com.pixelized.desktop.lwa.ui.screen.gamemaster.character -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterUio -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterUio.Action -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagUio +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterItemUio +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterItemUio.Action +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagItemUio import com.pixelized.desktop.lwa.utils.extention.unAccent import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetPreview @@ -11,14 +11,14 @@ import lwacharactersheet.composeapp.generated.resources.game_master__character_t import lwacharactersheet.composeapp.generated.resources.game_master__character_tag__npc import org.jetbrains.compose.resources.getString -class GameMasterFactory { +class GMCharacterFactory { suspend fun convertToGMCharacterPreviewUio( campaign: Campaign, characters: List, filter: String, - tags: Map, - ): List { + tags: Map, + ): List { val normalizedFilter = filter.unAccent() return characters.mapNotNull { @@ -35,8 +35,8 @@ class GameMasterFactory { campaign: Campaign, character: CharacterSheetPreview, filter: String, - tags: Map, - ): GMCharacterUio? { + tags: Map, + ): GMCharacterItemUio? { // get the characterInstanceId from the player list corresponding to this CharacterSheet if any val isPlayer = campaign.characters.firstOrNull { it == character.characterSheetId @@ -55,30 +55,30 @@ class GameMasterFactory { } } // Tag filter process : Player. - if (tags[GMTagUio.TagId.PLAYER] == true && isPlayer.not()) { + if (tags[GMTagItemUio.TagId.PLAYER] == true && isPlayer.not()) { return null } // Tag filter process : Npc. - if (tags[GMTagUio.TagId.NPC] == true && isNpc.not()) { + if (tags[GMTagItemUio.TagId.NPC] == true && isNpc.not()) { return null } // Build the call tag list. val previewTagsList = buildList { if (isPlayer) { add( - GMTagUio( - id = GMTagUio.TagId.PLAYER, + GMTagItemUio( + id = GMTagItemUio.TagId.PLAYER, label = getString(Res.string.game_master__character_tag__character), - highlight = tags[GMTagUio.TagId.PLAYER] ?: false, + highlight = tags[GMTagItemUio.TagId.PLAYER] ?: false, ) ) } if (isNpc) { add( - GMTagUio( - id = GMTagUio.TagId.NPC, + GMTagItemUio( + id = GMTagItemUio.TagId.NPC, label = getString(Res.string.game_master__character_tag__npc), - highlight = tags[GMTagUio.TagId.NPC] ?: false, + highlight = tags[GMTagItemUio.TagId.NPC] ?: false, ) ) } @@ -96,7 +96,7 @@ class GameMasterFactory { } } // return the cell UIO. - return GMCharacterUio( + return GMCharacterItemUio( characterSheetId = character.characterSheetId, name = character.name, level = character.level, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterPage.kt new file mode 100644 index 0000000..9329f74 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterPage.kt @@ -0,0 +1,165 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.character + +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.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Button +import androidx.compose.material.Icon +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.LocalWindowController +import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio +import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacter +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterItemUio +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMFilterHeader +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagItemUio +import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__create__title +import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__edit__title +import lwacharactersheet.composeapp.generated.resources.game_master__create_character_sheet +import org.jetbrains.compose.resources.getString +import org.jetbrains.compose.resources.stringResource +import org.koin.compose.viewmodel.koinViewModel + +@Composable +fun GMCharacterPage( + viewModel: GMCharacterViewModel = koinViewModel(), +) { + val windows = LocalWindowController.current + val scope = rememberCoroutineScope() + + val characters = viewModel.characters.collectAsState() + val tags = viewModel.tags.collectAsState() + + GMCharacterContent( + filter = viewModel.filter, + tags = tags, + characters = characters, + onTag = viewModel::onTag, + onCharacterAction = viewModel::onCharacterAction, + onCharacterSheetEdit = { characterSheetId -> + scope.launch { + windows.navigateToCharacterSheetEdit( + characterId = characterSheetId, + title = getString(Res.string.character_sheet_edit__edit__title), + ) + } + }, + onCharacterSheetCreate = { + scope.launch { + windows.navigateToCharacterSheetEdit( + characterId = null, + title = getString(Res.string.character_sheet_edit__create__title), + ) + } + }, + ) +} + +@Composable +fun GMCharacterContent( + modifier: Modifier = Modifier, + padding: Dp = 8.dp, + spacing: Dp = 8.dp, + filter: LwaTextFieldUio, + tags: State>, + characters: State>, + onTag: (GMTagItemUio.TagId) -> Unit, + onCharacterAction: (String, GMCharacterItemUio.Action) -> Unit, + onCharacterSheetEdit: (String) -> Unit, + onCharacterSheetCreate: () -> Unit, +) { + Column( + modifier = modifier, + ) { + Surface( + elevation = 1.dp, + ) { + GMFilterHeader( + padding = padding, + spacing = spacing, + filter = filter, + tags = tags, + onTag = onTag, + ) + } + Box( + modifier = Modifier.fillMaxWidth().weight(1f), + ) { + LazyColumn( + modifier = Modifier.matchParentSize(), + contentPadding = remember { + PaddingValues( + start = padding, + top = padding, + end = padding, + bottom = padding + 48.dp + padding, + ) + }, + verticalArrangement = Arrangement.spacedBy(space = spacing), + ) { + items( + items = characters.value, + key = { it.characterSheetId }, + ) { character -> + GMCharacter( + modifier = Modifier + .fillMaxWidth() + .animateItem(), + character = character, + onEdit = { + onCharacterSheetEdit(character.characterSheetId) + }, + onAction = { action -> + onCharacterAction(character.characterSheetId, action) + }, + onTag = onTag, + ) + } + } + Row( + modifier = Modifier + .align(alignment = Alignment.BottomEnd) + .padding(all = padding), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Button( + colors = LwaButtonColors(), + shape = CircleShape, + onClick = onCharacterSheetCreate, + ) { + Text( + modifier = Modifier.padding(end = 4.dp), + text = stringResource(Res.string.game_master__create_character_sheet), + ) + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterViewModel.kt new file mode 100644 index 0000000..dc992c2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/character/GMCharacterViewModel.kt @@ -0,0 +1,122 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.character + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +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.desktop.lwa.ui.composable.textfield.LwaTextFieldUio +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterItemUio.Action +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagItemUio +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMTagItemUio.TagId +import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.game_master__character__filter +import lwacharactersheet.composeapp.generated.resources.game_master__character_tag__character +import lwacharactersheet.composeapp.generated.resources.game_master__character_tag__npc +import org.jetbrains.compose.resources.getString + +class GMCharacterViewModel( + private val networkRepository: NetworkRepository, + private val campaignRepository: CampaignRepository, + characterSheetRepository: CharacterSheetRepository, + private val factory: GMCharacterFactory, +) : ViewModel() { + + private val _filter = MutableStateFlow("") + val filter = LwaTextFieldUio( + enable = true, + labelFlow = MutableStateFlow(runBlocking { getString(Res.string.game_master__character__filter) }), + valueFlow = _filter, + isError = MutableStateFlow(false), + placeHolderFlow = MutableStateFlow(null), + onValueChange = { _filter.value = it }, + ) + + private val _tags = MutableStateFlow(mapOf(TagId.PLAYER to false, TagId.NPC to false)) + val tags = _tags.map { it: Map -> + it.map { (tag, highlight) -> + when (tag) { + TagId.PLAYER -> GMTagItemUio( + id = TagId.PLAYER, + label = getString(Res.string.game_master__character_tag__character), + highlight = highlight, + ) + + TagId.NPC -> GMTagItemUio( + id = TagId.NPC, + label = getString(Res.string.game_master__character_tag__npc), + highlight = highlight, + ) + } + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = emptyList(), + ) + + val characters = combine( + campaignRepository.campaignFlow, + characterSheetRepository.characterSheetPreviewFlow, + filter.valueFlow, + _tags, + factory::convertToGMCharacterPreviewUio, + ).stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = emptyList(), + ) + + fun onCharacterAction( + characterSheetId: String, + action: Action, + ) { + viewModelScope.launch { + try { + when (action) { + Action.DisplayPortrait -> networkRepository.share( + GameMasterEvent.DisplayPortrait( + timestamp = System.currentTimeMillis(), + characterSheetId = characterSheetId, + ) + ) + + Action.AddToGroup -> campaignRepository.addCharacter( + characterSheetId = characterSheetId, + ) + + Action.AddToNpc -> campaignRepository.addNpc( + characterSheetId = characterSheetId, + ) + + Action.RemoveFromGroup -> campaignRepository.removeCharacter( + characterSheetId = characterSheetId, + ) + + Action.RemoveFromNpc -> campaignRepository.removeNpc( + characterSheetId = characterSheetId, + ) + } + } catch (exception: Exception) { + // TODO + } + } + } + + fun onTag( + id: TagId, + ) { + _tags.value = _tags.value.toMutableMap().also { + it[id] = it.getOrPut(id) { true }.not() + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMAction.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMAction.kt new file mode 100644 index 0000000..6bb44d1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMAction.kt @@ -0,0 +1,60 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.items + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.minimumInteractiveComponentSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.lwa +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource + + +@Stable +object GmActionDefault { + val padding = PaddingValues(horizontal = 16.dp) +} + +@Composable +fun GMAction( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = GmActionDefault.padding, + icon: DrawableResource, + label: String, + onAction: () -> Unit, +) { + Row( + modifier = Modifier + .clip(shape = MaterialTheme.lwa.shapes.gameMaster) + .clickable(onClick = onAction) + .background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp) + .minimumInteractiveComponentSize() + .padding(paddingValues = paddingValues) + .then(other = modifier), + horizontalArrangement = Arrangement.spacedBy(space = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(icon), + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null, + ) + Text( + style = MaterialTheme.lwa.typography.base.body1, + text = label, + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMCharacter.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMCharacter.kt index ec8cdf7..7f1afc6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMCharacter.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMCharacter.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterUio.Action +import com.pixelized.desktop.lwa.ui.screen.gamemaster.items.GMCharacterItemUio.Action import com.pixelized.desktop.lwa.ui.theme.lwa import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.game_master__character_action__add_to_group @@ -46,11 +46,11 @@ import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource @Stable -data class GMCharacterUio( +data class GMCharacterItemUio( val characterSheetId: String, val name: String, val level: Int, - val tags: List, + val tags: List, val actions: List, ) { @Stable @@ -92,16 +92,17 @@ object GMCharacterPreviewDefault { fun GMCharacter( modifier: Modifier = Modifier, padding: PaddingValues = GMCharacterPreviewDefault.padding, - character: GMCharacterUio, + character: GMCharacterItemUio, onEdit: () -> Unit, onAction: (Action) -> Unit, + onTag: (GMTagItemUio.TagId) -> Unit, ) { val layoutDirection = LocalLayoutDirection.current val startPadding = padding.calculateStartPadding(layoutDirection) Box( modifier = Modifier - .clip(shape = remember { RoundedCornerShape(8.dp) }) + .clip(shape = MaterialTheme.lwa.shapes.gameMaster) .clickable(onClick = onEdit) .background(color = MaterialTheme.lwa.colorScheme.elevated.base1dp) .then(other = modifier), @@ -109,6 +110,7 @@ fun GMCharacter( Row( modifier = Modifier.padding(start = startPadding), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = 4.dp), ) { Row( modifier = Modifier.weight(weight = 1f), @@ -133,6 +135,7 @@ fun GMCharacter( GMTag( elevation = 4.dp, tag = tag, + onTag = { onTag(tag.id) } ) } @@ -147,7 +150,7 @@ fun GMCharacter( @Composable private fun OverflowActionMenu( modifier: Modifier = Modifier, - character: GMCharacterUio, + character: GMCharacterItemUio, onAction: (Action) -> Unit, ) { val overflowMenu = remember(character) { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMFilterHeader.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMFilterHeader.kt new file mode 100644 index 0000000..b1e84ec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMFilterHeader.kt @@ -0,0 +1,100 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.items + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.draggable +import androidx.compose.foundation.gestures.rememberDraggableState +import androidx.compose.foundation.gestures.scrollBy +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField +import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio +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 org.jetbrains.compose.resources.painterResource + +@Composable +fun GMFilterHeader( + modifier: Modifier = Modifier, + lazyListState: LazyListState = rememberLazyListState(), + padding: Dp = 16.dp, + spacing: Dp = 8.dp, + filter: LwaTextFieldUio, + tags: State>, + onTag: (GMTagItemUio.TagId) -> Unit, +) { + val scope = rememberCoroutineScope() + + Column( + modifier = modifier, + ) { + LwaTextField( + modifier = Modifier.fillMaxWidth(), + field = filter, + trailingIcon = { + val value = filter.valueFlow.collectAsState() + + AnimatedVisibility( + visible = value.value.isNotBlank(), + enter = fadeIn(), + exit = fadeOut(), + ) { + IconButton( + onClick = { filter.onValueChange.invoke("") }, + ) { + Icon( + painter = painterResource(Res.drawable.ic_cancel_24dp), + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null, + ) + } + } + } + ) + LazyRow( + modifier = Modifier.draggable( + orientation = Orientation.Horizontal, + state = rememberDraggableState { delta -> + scope.launch { + lazyListState.scrollBy(-delta) + } + }, + ), + state = lazyListState, + contentPadding = remember(padding, spacing) { + PaddingValues(horizontal = padding, vertical = spacing) + }, + horizontalArrangement = Arrangement.spacedBy(space = spacing), + ) { + items( + items = tags.value, + ) { tag -> + GMTag( + tag = tag, + onTag = { onTag(tag.id) }, + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTab.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTab.kt new file mode 100644 index 0000000..1ef66f9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTab.kt @@ -0,0 +1,41 @@ +package com.pixelized.desktop.lwa.ui.screen.gamemaster.items + +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import com.pixelized.desktop.lwa.ui.theme.lwa +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.ic_account_child_invert_24dp +import lwacharactersheet.composeapp.generated.resources.ic_blur_on_24dp +import lwacharactersheet.composeapp.generated.resources.ic_iron_24dp +import lwacharactersheet.composeapp.generated.resources.ic_special_character_24dp +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource + +@Stable +enum class GMTabUio( + val icon: DrawableResource, +) { + Actions(icon = Res.drawable.ic_special_character_24dp), + Characters(icon = Res.drawable.ic_account_child_invert_24dp), + Alterations(icon = Res.drawable.ic_blur_on_24dp), + Objects(icon = Res.drawable.ic_iron_24dp), +} + +@Composable +fun GMTab( + tab: GMTabUio, + onClick: () -> Unit, +) { + IconButton( + onClick = onClick + ) { + Icon( + painter = painterResource(tab.icon), + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null, + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTagUio.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTagItemUio.kt similarity index 95% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTagUio.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTagItemUio.kt index 1204be8..fa860f7 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTagUio.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/items/GMTagItemUio.kt @@ -12,14 +12,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.pixelized.desktop.lwa.ui.theme.lwa @Stable -data class GMTagUio( +data class GMTagItemUio( val id: TagId, val label: String, val highlight: Boolean, @@ -41,7 +40,7 @@ fun GMTag( padding: PaddingValues = GmTagDefault.padding, shape: Shape = CircleShape, elevation: Dp = 2.dp, - tag: GMTagUio, + tag: GMTagItemUio, onTag: (() -> Unit)? = null, ) { val animatedColor = animateColorAsState( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt new file mode 100644 index 0000000..8a8b2b6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt @@ -0,0 +1,17 @@ +package com.pixelized.desktop.lwa.ui.theme.color.component + +import androidx.compose.material.ButtonColors +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.ContentAlpha +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable + +@Composable +@Stable +fun LwaButtonColors(): ButtonColors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.surface, + contentColor = MaterialTheme.colors.primary, + disabledContentColor = MaterialTheme.colors.surface.copy(alpha = ContentAlpha.disabled), +) + diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt index a6d9668..17dfada 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt @@ -12,6 +12,7 @@ data class LwaShapes( val portrait: Shape, val panel: Shape, val settings: Shape, + val gameMaster: Shape, ) @Stable @@ -20,10 +21,12 @@ fun lwaShapes( portrait: Shape = RoundedCornerShape(8.dp), panel: Shape = RoundedCornerShape(8.dp), settings: Shape = RoundedCornerShape(8.dp), + gameMaster: Shape = RoundedCornerShape(8.dp), ): LwaShapes = remember { LwaShapes( portrait = portrait, panel = panel, settings = settings, + gameMaster = gameMaster, ) }