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,
)
}