Add chat to the campaign screen + change a bit the network UI.
This commit is contained in:
parent
3f67e342a7
commit
7a9dd97123
29 changed files with 885 additions and 100 deletions
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M480,840q-42,0 -71,-29t-29,-71q0,-42 29,-71t71,-29q42,0 71,29t29,71q0,42 -29,71t-71,29ZM254,614l-84,-86q59,-59 138.5,-93.5T480,400q92,0 171.5,35T790,530l-84,84q-44,-44 -102,-69t-124,-25q-66,0 -124,25t-102,69ZM84,444 L0,360q92,-94 215,-147t265,-53q142,0 265,53t215,147l-84,84q-77,-77 -178.5,-120.5T480,280q-116,0 -217.5,43.5T84,444Z"
|
||||||
|
android:fillColor="#5f6368"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M790,904 L414,526q-47,11 -87.5,33T254,614l-84,-86q32,-32 69,-56t79,-42l-90,-90q-41,21 -76.5,46.5T84,444L0,358q32,-32 66.5,-57.5T140,252l-84,-84 56,-56 736,736 -58,56ZM480,840q-42,0 -71,-29.5T380,740q0,-42 29,-71t71,-29q42,0 71,29t29,71q0,41 -29,70.5T480,840ZM716,602 L687,573 658,544 514,400q81,8 151.5,41T790,528l-74,74ZM876,444q-77,-77 -178.5,-120.5T480,280q-21,0 -40.5,1.5T400,286L298,184q44,-12 89.5,-18t92.5,-6q142,0 265,53t215,145l-84,86Z"
|
||||||
|
android:fillColor="#5f6368"/>
|
||||||
|
</vector>
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -9,6 +9,7 @@
|
||||||
<string name="main_page__open_save_directory">Ouvrir le dossier de sauvegarde</string>
|
<string name="main_page__open_save_directory">Ouvrir le dossier de sauvegarde</string>
|
||||||
<string name="main_page__roll_history_action">Historique des lancers</string>
|
<string name="main_page__roll_history_action">Historique des lancers</string>
|
||||||
<string name="main_page__network_action">Configuration de la table</string>
|
<string name="main_page__network_action">Configuration de la table</string>
|
||||||
|
<string name="main_page__settings_action">Configuration de l'application</string>
|
||||||
|
|
||||||
<string name="roll_page__roll__label">Jet de :</string>
|
<string name="roll_page__roll__label">Jet de :</string>
|
||||||
<string name="roll_page__roll__success_label">Réussite si lancer inférieur ou égal à : %1$s</string>
|
<string name="roll_page__roll__success_label">Réussite si lancer inférieur ou égal à : %1$s</string>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.max
|
||||||
|
import androidx.compose.ui.unit.min
|
||||||
import androidx.compose.ui.window.ApplicationScope
|
import androidx.compose.ui.window.ApplicationScope
|
||||||
import androidx.compose.ui.window.Window
|
import androidx.compose.ui.window.Window
|
||||||
import androidx.compose.ui.window.rememberWindowState
|
import androidx.compose.ui.window.rememberWindowState
|
||||||
|
|
@ -41,6 +43,7 @@ import com.pixelized.desktop.lwa.ui.composable.key.LocalKeyEventHandlers
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.MainNavHost
|
import com.pixelized.desktop.lwa.ui.navigation.screen.MainNavHost
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetDestination
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetEditDestination
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetEditDestination
|
||||||
|
import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindowState
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.WindowController
|
import com.pixelized.desktop.lwa.ui.navigation.window.WindowController
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.WindowsNavHost
|
import com.pixelized.desktop.lwa.ui.navigation.window.WindowsNavHost
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheetEditWindow
|
import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheetEditWindow
|
||||||
|
|
@ -49,6 +52,8 @@ import com.pixelized.desktop.lwa.ui.navigation.window.destination.NetworkWindows
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.destination.RollHistoryWindow
|
import com.pixelized.desktop.lwa.ui.navigation.window.destination.RollHistoryWindow
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
|
import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
|
||||||
import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
|
||||||
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
|
|
@ -78,15 +83,27 @@ val LocalErrorSnackHost = compositionLocalOf<SnackbarHostState> {
|
||||||
error("Local Snack Controller is not yet ready")
|
error("Local Snack Controller is not yet ready")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val LocalApplicationScope = compositionLocalOf<ApplicationScope> {
|
||||||
|
error("Local application scope is not yet ready")
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun ApplicationScope.App() {
|
fun ApplicationScope.App() {
|
||||||
|
|
||||||
val maxWindowHeight = rememberMaxWindowHeight()
|
val maxWindowHeight = rememberMaxWindowHeight()
|
||||||
val snackHostState = remember { SnackbarHostState() }
|
val snackHostState = remember { SnackbarHostState() }
|
||||||
val errorSnackHostState = remember { SnackbarHostState() }
|
val errorSnackHostState = remember { SnackbarHostState() }
|
||||||
val windowController = remember { WindowController(maxWindowHeight) }
|
val windowController = remember { WindowController(maxWindowHeight) }
|
||||||
val keyEventHandlers = remember { mutableStateListOf<KeyEventHandler>() }
|
val keyEventHandlers = remember { mutableStateListOf<KeyEventHandler>() }
|
||||||
|
val windowsState = rememberWindowState(
|
||||||
|
size = DpSize(
|
||||||
|
width = 800.dp,
|
||||||
|
height = min(
|
||||||
|
a = 56.dp + PlayerRibbon.Default.size.height * 6 + 8.dp * 7 + 40.dp,
|
||||||
|
b = maxWindowHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Coil configuration
|
// Coil configuration
|
||||||
setSingletonImageLoaderFactory { context ->
|
setSingletonImageLoaderFactory { context ->
|
||||||
|
|
@ -96,14 +113,16 @@ fun ApplicationScope.App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
|
LocalApplicationScope provides this,
|
||||||
LocalSnackHost provides snackHostState,
|
LocalSnackHost provides snackHostState,
|
||||||
LocalErrorSnackHost provides errorSnackHostState,
|
LocalErrorSnackHost provides errorSnackHostState,
|
||||||
LocalWindowController provides windowController,
|
LocalWindowController provides windowController,
|
||||||
LocalKeyEventHandlers provides keyEventHandlers,
|
LocalKeyEventHandlers provides keyEventHandlers,
|
||||||
|
LocalWindowState provides windowsState,
|
||||||
) {
|
) {
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
state = rememberWindowState(size = DpSize(width = 800.dp, height = maxWindowHeight)),
|
state = windowsState,
|
||||||
title = runBlocking { getString(Res.string.app_name) },
|
title = runBlocking { getString(Res.string.app_name) },
|
||||||
onKeyEvent = { event ->
|
onKeyEvent = { event ->
|
||||||
keyEventHandlers.reversed().any { it(event) }
|
keyEventHandlers.reversed().any { it(event) }
|
||||||
|
|
@ -118,6 +137,7 @@ fun ApplicationScope.App() {
|
||||||
private fun MainWindowScreen(
|
private fun MainWindowScreen(
|
||||||
campaignViewModel: CampaignViewModel = koinViewModel(),
|
campaignViewModel: CampaignViewModel = koinViewModel(),
|
||||||
networkViewModel: NetworkViewModel = koinViewModel(),
|
networkViewModel: NetworkViewModel = koinViewModel(),
|
||||||
|
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
|
||||||
rollViewModel: RollHistoryViewModel = koinViewModel(),
|
rollViewModel: RollHistoryViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
@ -161,7 +181,11 @@ private fun MainWindowScreen(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
MainNavHost()
|
MainNavHost(
|
||||||
|
campaignViewModel = campaignViewModel,
|
||||||
|
networkViewModel = networkViewModel,
|
||||||
|
campaignChatViewModel = campaignChatViewModel,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
NetworkSnackHandler(
|
NetworkSnackHandler(
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ 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.CharacterDetailCharacteristicDialogViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogFactory
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialogFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.TextMessageFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailFactory
|
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.screen.campaign.player.detail.CharacterDetailViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
||||||
|
|
@ -105,6 +107,7 @@ val factoryDependencies
|
||||||
factoryOf(::PlayerRibbonFactory)
|
factoryOf(::PlayerRibbonFactory)
|
||||||
factoryOf(::CharacterDetailFactory)
|
factoryOf(::CharacterDetailFactory)
|
||||||
factoryOf(::CharacterSheetCharacteristicDialogFactory)
|
factoryOf(::CharacterSheetCharacteristicDialogFactory)
|
||||||
|
factoryOf(::TextMessageFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
val viewModelDependencies
|
val viewModelDependencies
|
||||||
|
|
@ -120,6 +123,7 @@ val viewModelDependencies
|
||||||
viewModelOf(::CharacterDetailViewModel)
|
viewModelOf(::CharacterDetailViewModel)
|
||||||
viewModelOf(::CharacterDiminishedViewModel)
|
viewModelOf(::CharacterDiminishedViewModel)
|
||||||
viewModelOf(::CharacterDetailCharacteristicDialogViewModel)
|
viewModelOf(::CharacterDetailCharacteristicDialogViewModel)
|
||||||
|
viewModelOf(::CampaignChatViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
val useCaseDependencies
|
val useCaseDependencies
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ import com.pixelized.desktop.lwa.ui.navigation.screen.destination.MainDestinatio
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableMainPage
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableMainPage
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableNetworkPage
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableNetworkPage
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableOldMainPage
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableOldMainPage
|
||||||
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.composableSettingsPage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
|
|
||||||
val LocalScreenController = compositionLocalOf<NavHostController> {
|
val LocalScreenController = compositionLocalOf<NavHostController> {
|
||||||
error("MainNavHost controller is not yet ready")
|
error("MainNavHost controller is not yet ready")
|
||||||
|
|
@ -18,6 +22,9 @@ val LocalScreenController = compositionLocalOf<NavHostController> {
|
||||||
@Composable
|
@Composable
|
||||||
fun MainNavHost(
|
fun MainNavHost(
|
||||||
controller: NavHostController = rememberNavController(),
|
controller: NavHostController = rememberNavController(),
|
||||||
|
campaignViewModel: CampaignViewModel,
|
||||||
|
networkViewModel: NetworkViewModel,
|
||||||
|
campaignChatViewModel: CampaignChatViewModel,
|
||||||
startDestination: String = MainDestination.navigationRoute(),
|
startDestination: String = MainDestination.navigationRoute(),
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
|
|
@ -27,9 +34,14 @@ fun MainNavHost(
|
||||||
navController = controller,
|
navController = controller,
|
||||||
startDestination = startDestination,
|
startDestination = startDestination,
|
||||||
) {
|
) {
|
||||||
composableMainPage()
|
composableMainPage(
|
||||||
|
campaignViewModel = campaignViewModel,
|
||||||
|
networkViewModel = networkViewModel,
|
||||||
|
campaignChatViewModel = campaignChatViewModel,
|
||||||
|
)
|
||||||
composableOldMainPage()
|
composableOldMainPage()
|
||||||
composableNetworkPage()
|
composableNetworkPage()
|
||||||
|
composableSettingsPage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,10 @@ package com.pixelized.desktop.lwa.ui.navigation.screen.destination
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.MainPage
|
import com.pixelized.desktop.lwa.ui.screen.campaign.MainPage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
|
|
||||||
object MainDestination {
|
object MainDestination {
|
||||||
private const val ROUTE = "main"
|
private const val ROUTE = "main"
|
||||||
|
|
@ -12,11 +15,19 @@ object MainDestination {
|
||||||
fun navigationRoute() = ROUTE
|
fun navigationRoute() = ROUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavGraphBuilder.composableMainPage() {
|
fun NavGraphBuilder.composableMainPage(
|
||||||
|
campaignViewModel: CampaignViewModel,
|
||||||
|
networkViewModel: NetworkViewModel,
|
||||||
|
campaignChatViewModel: CampaignChatViewModel,
|
||||||
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = MainDestination.baseRoute(),
|
route = MainDestination.baseRoute(),
|
||||||
) {
|
) {
|
||||||
MainPage()
|
MainPage(
|
||||||
|
campaignViewModel = campaignViewModel,
|
||||||
|
networkViewModel = networkViewModel,
|
||||||
|
campaignChatViewModel = campaignChatViewModel,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.navigation.screen.destination
|
||||||
|
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.settings.SettingsScreen
|
||||||
|
|
||||||
|
object SettingsDestination {
|
||||||
|
private const val ROUTE = "settings"
|
||||||
|
|
||||||
|
fun baseRoute() = ROUTE
|
||||||
|
fun navigationRoute() = ROUTE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavGraphBuilder.composableSettingsPage() {
|
||||||
|
composable(
|
||||||
|
route = SettingsDestination.baseRoute(),
|
||||||
|
) {
|
||||||
|
SettingsScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavHostController.navigateToSettings() {
|
||||||
|
val route = SettingsDestination.navigationRoute()
|
||||||
|
navigate(route = route)
|
||||||
|
}
|
||||||
|
|
@ -10,12 +10,14 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
|
@ -36,22 +38,32 @@ import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentControlle
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
|
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
|
||||||
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
|
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChat
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.CampaignChatViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbar
|
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbar
|
||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
|
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollPage
|
import com.pixelized.desktop.lwa.ui.screen.roll.RollPage
|
||||||
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
|
val LocalCampaignLayoutScope = compositionLocalOf<CampaignLayoutScope> {
|
||||||
|
error("LocalCampaignLayoutScope is not yet ready.")
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainPage(
|
fun MainPage(
|
||||||
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
|
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
|
||||||
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
|
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
|
||||||
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
|
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
|
||||||
|
campaignViewModel: CampaignViewModel = koinViewModel(),
|
||||||
|
networkViewModel: NetworkViewModel = koinViewModel(),
|
||||||
|
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
|
||||||
rollViewModel: RollViewModel = koinViewModel(),
|
rollViewModel: RollViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
KeyHandler {
|
KeyHandler {
|
||||||
|
|
@ -78,20 +90,21 @@ fun MainPage(
|
||||||
CampaignScreenLayout(
|
CampaignScreenLayout(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
top = {
|
top = {
|
||||||
CampaignToolbar()
|
CampaignToolbar(
|
||||||
|
campaignViewModel = campaignViewModel,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
bottom = {
|
bottom = {
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
// .height(48.dp)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
elevation = 1.dp,
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
main = {
|
main = {
|
||||||
|
|
||||||
|
},
|
||||||
|
chat = {
|
||||||
|
CampaignChat(
|
||||||
|
modifier = Modifier.padding(all = 8.dp),
|
||||||
|
campaignChatViewModel = campaignChatViewModel,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
leftOverlay = {
|
leftOverlay = {
|
||||||
PlayerRibbon(
|
PlayerRibbon(
|
||||||
|
|
@ -104,9 +117,9 @@ fun MainPage(
|
||||||
rightOverlay = {
|
rightOverlay = {
|
||||||
CharacterDetailPanel(
|
CharacterDetailPanel(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.padding(all = 8.dp)
|
||||||
.width(width = 128.dp * 4)
|
.width(width = 128.dp * 4)
|
||||||
.fillMaxHeight()
|
.fillMaxHeight(),
|
||||||
.padding(all = 8.dp),
|
|
||||||
blurController = blurController,
|
blurController = blurController,
|
||||||
detailViewModel = characterDetailViewModel,
|
detailViewModel = characterDetailViewModel,
|
||||||
rollViewModel = rollViewModel,
|
rollViewModel = rollViewModel,
|
||||||
|
|
@ -182,22 +195,27 @@ fun MainPage(
|
||||||
@Composable
|
@Composable
|
||||||
private fun CampaignScreenLayout(
|
private fun CampaignScreenLayout(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
top: @Composable CampaignScreenScope.() -> Unit,
|
top: @Composable () -> Unit,
|
||||||
bottom: @Composable CampaignScreenScope.() -> Unit,
|
bottom: @Composable () -> Unit,
|
||||||
main: @Composable CampaignScreenScope.() -> Unit,
|
main: @Composable () -> Unit,
|
||||||
leftOverlay: @Composable CampaignScreenScope.() -> Unit,
|
chat: @Composable () -> Unit,
|
||||||
rightOverlay: @Composable CampaignScreenScope.() -> Unit,
|
leftOverlay: @Composable () -> Unit,
|
||||||
|
rightOverlay: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val leftOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
val leftOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
||||||
val rightOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
val rightOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
||||||
|
val chatOverlayState = remember { mutableStateOf(DpSize.Unspecified) }
|
||||||
val scope = remember {
|
val scope = remember {
|
||||||
CampaignScreenScope(
|
CampaignLayoutScope(
|
||||||
leftOverlay = leftOverlayState,
|
leftOverlay = leftOverlayState,
|
||||||
rightOverlay = rightOverlayState,
|
rightOverlay = rightOverlayState,
|
||||||
|
chatOverlay = chatOverlayState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
with(scope) {
|
CompositionLocalProvider(
|
||||||
|
LocalCampaignLayoutScope provides scope,
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -212,6 +230,13 @@ private fun CampaignScreenLayout(
|
||||||
) {
|
) {
|
||||||
main()
|
main()
|
||||||
}
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(alignment = Alignment.BottomEnd)
|
||||||
|
.onSizeChanged { }
|
||||||
|
) {
|
||||||
|
chat()
|
||||||
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(alignment = Alignment.CenterStart)
|
.align(alignment = Alignment.CenterStart)
|
||||||
|
|
@ -232,9 +257,11 @@ private fun CampaignScreenLayout(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CampaignScreenScope(
|
@Stable
|
||||||
|
data class CampaignLayoutScope(
|
||||||
val leftOverlay: State<DpSize>,
|
val leftOverlay: State<DpSize>,
|
||||||
val rightOverlay: State<DpSize>,
|
val rightOverlay: State<DpSize>,
|
||||||
|
val chatOverlay: State<DpSize>,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun IntSize.toDp(density: Density) = with(density) {
|
private fun IntSize.toDp(density: Density) = with(density) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ class CampaignViewModel(
|
||||||
val title: Flow<String> = campaignRepository.campaignFlow
|
val title: Flow<String> = campaignRepository.campaignFlow
|
||||||
.map { it.scene.name }
|
.map { it.scene.name }
|
||||||
|
|
||||||
|
val networkStatus = network.status
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
combine(
|
combine(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.chat
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.input.pointer.PointerEventType
|
||||||
|
import androidx.compose.ui.input.pointer.onPointerEvent
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.max
|
||||||
|
import androidx.compose.ui.unit.min
|
||||||
|
import androidx.compose.ui.window.WindowState
|
||||||
|
import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindowState
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignLayoutScope
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.LocalCampaignLayoutScope
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.RollTextMessage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.RollTextMessageUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.TextMessage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun CampaignChat(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
campaignChatViewModel: CampaignChatViewModel = koinViewModel(),
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val lazyState = rememberLazyListState()
|
||||||
|
val animatedChatWidth = rememberAnimatedChatWidth()
|
||||||
|
val colorScheme = MaterialTheme.lwa.colorScheme
|
||||||
|
val messages = campaignChatViewModel.messages.collectAsState()
|
||||||
|
|
||||||
|
ChatScrollDownEffect(
|
||||||
|
lazyState = lazyState,
|
||||||
|
messages = messages,
|
||||||
|
displayChat = campaignChatViewModel::displayChat,
|
||||||
|
hideChat = campaignChatViewModel::hideChat,
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(
|
||||||
|
width = animatedChatWidth.value,
|
||||||
|
height = PlayerRibbon.Default.size.height * 2 + 8.dp,
|
||||||
|
)
|
||||||
|
.graphicsLayer {
|
||||||
|
alpha = campaignChatViewModel.chatAnimatedVisibility.value
|
||||||
|
}
|
||||||
|
.background(
|
||||||
|
shape = remember { RoundedCornerShape(8.dp) },
|
||||||
|
color = remember { colorScheme.elevated.base1dp.copy(alpha = 0.5f) },
|
||||||
|
)
|
||||||
|
.onPointerEvent(eventType = PointerEventType.Enter) {
|
||||||
|
scope.launch { campaignChatViewModel.displayChat() }
|
||||||
|
}
|
||||||
|
.onPointerEvent(eventType = PointerEventType.Exit) {
|
||||||
|
scope.launch { campaignChatViewModel.hideChat() }
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
state = lazyState,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(
|
||||||
|
space = 4.dp,
|
||||||
|
alignment = Alignment.Bottom,
|
||||||
|
),
|
||||||
|
contentPadding = remember { PaddingValues(all = 8.dp) },
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = messages.value,
|
||||||
|
key = { it.id },
|
||||||
|
contentType = { it.javaClass.simpleName }
|
||||||
|
) {
|
||||||
|
when (it) {
|
||||||
|
is RollTextMessageUio -> RollTextMessage(message = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChatScrollDownEffect(
|
||||||
|
lazyState: LazyListState,
|
||||||
|
messages: State<List<TextMessage>>,
|
||||||
|
displayChat: suspend () -> Unit,
|
||||||
|
hideChat: suspend () -> Unit,
|
||||||
|
) {
|
||||||
|
LaunchedEffect(
|
||||||
|
key1 = messages.value.lastOrNull()?.id,
|
||||||
|
) {
|
||||||
|
if (messages.value.isNotEmpty()) {
|
||||||
|
displayChat()
|
||||||
|
lazyState.animateScrollToItem(
|
||||||
|
index = messages.value.lastIndex + 1,
|
||||||
|
)
|
||||||
|
hideChat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
private fun rememberAnimatedChatWidth(
|
||||||
|
campaignScreenScope: CampaignLayoutScope = LocalCampaignLayoutScope.current,
|
||||||
|
windowsState: WindowState = LocalWindowState.current,
|
||||||
|
): State<Dp> {
|
||||||
|
val chatWidth = remember(windowsState, campaignScreenScope) {
|
||||||
|
derivedStateOf {
|
||||||
|
val minChatWidth = 64.dp * 8
|
||||||
|
val maxChatWidth = 64.dp * 12
|
||||||
|
val windowWidth = windowsState.size.width
|
||||||
|
if (windowWidth != Dp.Unspecified) {
|
||||||
|
val width = windowWidth - campaignScreenScope.leftOverlay.value.width - 16.dp
|
||||||
|
min(max(width, minChatWidth), maxChatWidth)
|
||||||
|
} else {
|
||||||
|
minChatWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return animateDpAsState(targetValue = chatWidth.value)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.chat
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.TextMessage
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
|
||||||
|
class CampaignChatViewModel(
|
||||||
|
networkRepository: NetworkRepository,
|
||||||
|
textMessageFactory: TextMessageFactory,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val chatAnimatedVisibility = Animatable(0f)
|
||||||
|
|
||||||
|
private var _messages = emptyList<TextMessage>()
|
||||||
|
val messages: StateFlow<List<TextMessage>> = networkRepository.data
|
||||||
|
.mapNotNull { message ->
|
||||||
|
val text = textMessageFactory
|
||||||
|
.convertToTextMessage(message = message)
|
||||||
|
?: return@mapNotNull _messages
|
||||||
|
|
||||||
|
_messages = _messages.toMutableList().also {
|
||||||
|
it.add(index = it.lastIndex + 1, element = text)
|
||||||
|
}
|
||||||
|
_messages
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = _messages,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun displayChat() {
|
||||||
|
chatAnimatedVisibility.animateTo(1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun hideChat() {
|
||||||
|
chatAnimatedVisibility.animateTo(0f, animationSpec = tween(2000, delayMillis = 8000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.chat
|
||||||
|
|
||||||
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.RollTextMessageUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.TextMessage
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.Message
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.payload.CampaignMessage
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.payload.RestSynchronisation
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.payload.RollMessage
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.payload.UpdateSkillUsageMessage
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
|
class TextMessageFactory(
|
||||||
|
private val characterSheetRepository: CharacterSheetRepository,
|
||||||
|
) {
|
||||||
|
private val formatId = SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS")
|
||||||
|
private val formatTime = SimpleDateFormat("HH:mm:ss")
|
||||||
|
|
||||||
|
fun convertToTextMessage(
|
||||||
|
message: Message,
|
||||||
|
): TextMessage? {
|
||||||
|
val time = System.currentTimeMillis()
|
||||||
|
val id = formatId.format(time)
|
||||||
|
return when (val payload = message.value) {
|
||||||
|
is RollMessage -> {
|
||||||
|
val sheetPreview = characterSheetRepository
|
||||||
|
.characterPreview(characterId = payload.characterId)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
RollTextMessageUio(
|
||||||
|
id = id,
|
||||||
|
timestamp = formatTime.format(time),
|
||||||
|
character = sheetPreview.name,
|
||||||
|
skillLabel = payload.skillLabel,
|
||||||
|
rollDifficulty = payload.rollDifficulty,
|
||||||
|
rollValue = payload.rollValue,
|
||||||
|
rollSuccessLimit = payload.rollSuccessLimit,
|
||||||
|
resultLabel = payload.resultLabel,
|
||||||
|
resultType = when (payload.critical) {
|
||||||
|
RollMessage.Critical.CRITICAL_SUCCESS -> RollTextMessageUio.Critical.CRITICAL_SUCCESS
|
||||||
|
RollMessage.Critical.SPECIAL_SUCCESS -> RollTextMessageUio.Critical.SPECIAL_SUCCESS
|
||||||
|
RollMessage.Critical.SUCCESS -> RollTextMessageUio.Critical.SUCCESS
|
||||||
|
RollMessage.Critical.FAILURE -> RollTextMessageUio.Critical.FAILURE
|
||||||
|
RollMessage.Critical.CRITICAL_FAILURE -> RollTextMessageUio.Critical.CRITICAL_FAILURE
|
||||||
|
null -> null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is CampaignMessage.UpdateCharacteristic -> null
|
||||||
|
is CampaignMessage.UpdateDiminished -> null
|
||||||
|
RestSynchronisation.Campaign -> null
|
||||||
|
is RestSynchronisation.CharacterDelete -> null
|
||||||
|
is RestSynchronisation.CharacterUpdate -> null
|
||||||
|
is RestSynchronisation.ToggleActiveAlteration -> null
|
||||||
|
is UpdateSkillUsageMessage -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.chat.text
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.roll_history__item__difficulty
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.roll_history__item__throw
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class RollTextMessageUio(
|
||||||
|
override val id: String,
|
||||||
|
override val timestamp: String,
|
||||||
|
val character: String,
|
||||||
|
val skillLabel: String,
|
||||||
|
val rollDifficulty: String?,
|
||||||
|
val rollValue: Int,
|
||||||
|
val rollSuccessLimit: Int?,
|
||||||
|
val resultLabel: String?,
|
||||||
|
val resultType: Critical?,
|
||||||
|
) : TextMessage {
|
||||||
|
enum class Critical {
|
||||||
|
CRITICAL_SUCCESS,
|
||||||
|
SPECIAL_SUCCESS,
|
||||||
|
SUCCESS,
|
||||||
|
FAILURE,
|
||||||
|
CRITICAL_FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RollTextMessage(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
message: RollTextMessageUio,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(space = 3.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.timestamp,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = message.timestamp,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.timestamp,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = ">",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = message.character,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
fontWeight = FontWeight.ExtraLight,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = stringResource(Res.string.roll_history__item__throw),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = message.skillLabel,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = "-",
|
||||||
|
)
|
||||||
|
message.resultLabel?.let {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
color = when (message.resultType) {
|
||||||
|
RollTextMessageUio.Critical.CRITICAL_SUCCESS -> MaterialTheme.lwa.colorScheme.chat.criticalSuccess
|
||||||
|
RollTextMessageUio.Critical.SPECIAL_SUCCESS -> MaterialTheme.lwa.colorScheme.chat.spacialSuccess
|
||||||
|
RollTextMessageUio.Critical.SUCCESS -> MaterialTheme.lwa.colorScheme.chat.success
|
||||||
|
RollTextMessageUio.Critical.FAILURE -> MaterialTheme.lwa.colorScheme.chat.failure
|
||||||
|
RollTextMessageUio.Critical.CRITICAL_FAILURE -> MaterialTheme.lwa.colorScheme.chat.criticalFailure
|
||||||
|
null -> MaterialTheme.lwa.colorScheme.chat.text
|
||||||
|
},
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = "${message.rollValue}",
|
||||||
|
)
|
||||||
|
message.rollSuccessLimit?.let {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = "/$it",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message.rollDifficulty?.let {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = "-",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = stringResource(Res.string.roll_history__item__difficulty),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.lwa.typography.chat.text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.chat.text
|
||||||
|
|
||||||
|
sealed interface TextMessage {
|
||||||
|
val id : String
|
||||||
|
val timestamp: String
|
||||||
|
}
|
||||||
|
|
@ -118,6 +118,7 @@ fun CharacterDetailPanel(
|
||||||
blurController.show()
|
blurController.show()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,8 @@ fun PlayerPortraitRoll(
|
||||||
.onClick(
|
.onClick(
|
||||||
matcher = PointerMatcher.mouse(PointerButton.Secondary),
|
matcher = PointerMatcher.mouse(PointerButton.Secondary),
|
||||||
onClick = { onRightClick(it) },
|
onClick = { onRightClick(it) },
|
||||||
).clickable {
|
)
|
||||||
|
.clickable {
|
||||||
onLeftClick(it)
|
onLeftClick(it)
|
||||||
}
|
}
|
||||||
.padding(all = 8.dp),
|
.padding(all = 8.dp),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar
|
package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.DropdownMenu
|
import androidx.compose.material.DropdownMenu
|
||||||
import androidx.compose.material.DropdownMenuItem
|
import androidx.compose.material.DropdownMenuItem
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
|
|
@ -16,19 +17,24 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.pixelized.desktop.lwa.LocalWindowController
|
import com.pixelized.desktop.lwa.LocalWindowController
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
|
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToOldMainPage
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToOldMainPage
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToNetwork
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToSettings
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToRollHistory
|
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToRollHistory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_settings_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_settings_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_table_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_table_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_timeline_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_timeline_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.main_page__network_action
|
import lwacharactersheet.composeapp.generated.resources.ic_wifi_24dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_wifi_off_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.main_page__roll_history_action
|
import lwacharactersheet.composeapp.generated.resources.main_page__roll_history_action
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
|
@ -37,30 +43,76 @@ import org.koin.compose.viewmodel.koinViewModel
|
||||||
@Composable
|
@Composable
|
||||||
fun CampaignToolbar(
|
fun CampaignToolbar(
|
||||||
campaignViewModel: CampaignViewModel = koinViewModel(),
|
campaignViewModel: CampaignViewModel = koinViewModel(),
|
||||||
|
networkViewModel: NetworkViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
val windows = LocalWindowController.current
|
val windows = LocalWindowController.current
|
||||||
val screen = LocalScreenController.current
|
val screen = LocalScreenController.current
|
||||||
|
|
||||||
val isOverflowMenuOpen = remember { mutableStateOf(false) }
|
val isOverflowMenuOpen = remember { mutableStateOf(false) }
|
||||||
|
val isNetworkMenuOpen = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
CampaignToolbarContent(
|
CampaignToolbarContent(
|
||||||
title = campaignViewModel.title.collectAsState(initial = ""),
|
title = campaignViewModel.title.collectAsState(initial = ""),
|
||||||
|
networkStatus = campaignViewModel.networkStatus.collectAsState(),
|
||||||
|
isNetworkMenuOpen = isNetworkMenuOpen,
|
||||||
isOverflowMenuOpen = isOverflowMenuOpen,
|
isOverflowMenuOpen = isOverflowMenuOpen,
|
||||||
|
networkMenu = {
|
||||||
|
NetworkPage(
|
||||||
|
modifier = Modifier.size(384.dp, 240.dp),
|
||||||
|
viewModel = networkViewModel
|
||||||
|
)
|
||||||
|
},
|
||||||
|
overflowMenu = {
|
||||||
|
DropdownMenuItem(
|
||||||
|
onClick = {
|
||||||
|
isOverflowMenuOpen.value = false
|
||||||
|
windows.navigateToRollHistory()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(Res.drawable.ic_timeline_24dp),
|
||||||
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
text = stringResource(Res.string.main_page__roll_history_action),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenuItem(
|
||||||
|
onClick = {
|
||||||
|
isOverflowMenuOpen.value = false
|
||||||
|
screen.navigateToOldMainPage()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(Res.drawable.ic_table_24dp),
|
||||||
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
text = "Ancienne interface utilisateur",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onNetwork = {
|
||||||
|
isNetworkMenuOpen.value = true
|
||||||
|
},
|
||||||
onOverflow = {
|
onOverflow = {
|
||||||
isOverflowMenuOpen.value = isOverflowMenuOpen.value.not()
|
isOverflowMenuOpen.value = isOverflowMenuOpen.value.not()
|
||||||
},
|
},
|
||||||
|
onSettings = {
|
||||||
|
screen.navigateToSettings()
|
||||||
|
},
|
||||||
|
onDismissNetworkMenu = {
|
||||||
|
isNetworkMenuOpen.value = false
|
||||||
|
},
|
||||||
onDismissOverflowMenu = {
|
onDismissOverflowMenu = {
|
||||||
isOverflowMenuOpen.value = false
|
isOverflowMenuOpen.value = false
|
||||||
},
|
},
|
||||||
onRollHistory = {
|
|
||||||
windows.navigateToRollHistory()
|
|
||||||
},
|
|
||||||
onNetwork = {
|
|
||||||
windows.navigateToNetwork()
|
|
||||||
},
|
|
||||||
onOlUi = {
|
|
||||||
screen.navigateToOldMainPage()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,12 +120,16 @@ fun CampaignToolbar(
|
||||||
private fun CampaignToolbarContent(
|
private fun CampaignToolbarContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
title: State<String>,
|
title: State<String>,
|
||||||
|
networkStatus: State<NetworkRepository.Status>,
|
||||||
|
isNetworkMenuOpen: State<Boolean>,
|
||||||
isOverflowMenuOpen: State<Boolean>,
|
isOverflowMenuOpen: State<Boolean>,
|
||||||
onOverflow: () -> Unit,
|
networkMenu: @Composable () -> Unit,
|
||||||
onDismissOverflowMenu: () -> Unit,
|
overflowMenu: @Composable () -> Unit,
|
||||||
onRollHistory: () -> Unit,
|
|
||||||
onNetwork: () -> Unit,
|
onNetwork: () -> Unit,
|
||||||
onOlUi: () -> Unit,
|
onOverflow: () -> Unit,
|
||||||
|
onSettings: () -> Unit,
|
||||||
|
onDismissNetworkMenu: () -> Unit,
|
||||||
|
onDismissOverflowMenu: () -> Unit,
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -83,6 +139,38 @@ private fun CampaignToolbarContent(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
IconButton(
|
||||||
|
onClick = onNetwork
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
when (networkStatus.value) {
|
||||||
|
NetworkRepository.Status.CONNECTED -> Res.drawable.ic_wifi_24dp
|
||||||
|
NetworkRepository.Status.DISCONNECTED -> Res.drawable.ic_wifi_off_24dp
|
||||||
|
}
|
||||||
|
),
|
||||||
|
tint = when (networkStatus.value) {
|
||||||
|
NetworkRepository.Status.CONNECTED -> MaterialTheme.lwa.colorScheme.base.primary
|
||||||
|
NetworkRepository.Status.DISCONNECTED -> MaterialTheme.lwa.colorScheme.base.error
|
||||||
|
},
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
offset = remember { DpOffset(x = -(48.dp * 2 + 8.dp), y = 8.dp) },
|
||||||
|
expanded = isNetworkMenuOpen.value,
|
||||||
|
onDismissRequest = onDismissNetworkMenu,
|
||||||
|
content = { networkMenu() },
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = onSettings
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(Res.drawable.ic_settings_24dp),
|
||||||
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onOverflow,
|
onClick = onOverflow,
|
||||||
) {
|
) {
|
||||||
|
|
@ -92,54 +180,12 @@ private fun CampaignToolbarContent(
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
|
offset = remember { DpOffset(x = (-8).dp, y = 8.dp) },
|
||||||
expanded = isOverflowMenuOpen.value,
|
expanded = isOverflowMenuOpen.value,
|
||||||
onDismissRequest = onDismissOverflowMenu,
|
onDismissRequest = onDismissOverflowMenu,
|
||||||
) {
|
content = { overflowMenu() },
|
||||||
DropdownMenuItem(
|
)
|
||||||
onClick = { onDismissOverflowMenu(); onRollHistory() },
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_timeline_24dp),
|
|
||||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
|
||||||
color = MaterialTheme.colors.primary,
|
|
||||||
text = stringResource(Res.string.main_page__roll_history_action),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DropdownMenuItem(
|
|
||||||
onClick = { onDismissOverflowMenu(); onNetwork() },
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_settings_24dp),
|
|
||||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
|
||||||
color = MaterialTheme.colors.primary,
|
|
||||||
text = stringResource(Res.string.main_page__network_action),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DropdownMenuItem(
|
|
||||||
onClick = { onDismissOverflowMenu(); onOlUi() },
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_table_24dp),
|
|
||||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
|
||||||
color = MaterialTheme.colors.primary,
|
|
||||||
text = "Ancienne interface utilisateur",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +64,7 @@ data class NetworkPageUio(
|
||||||
val enableCancel: Boolean,
|
val enableCancel: Boolean,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
@Stable
|
||||||
fun empty(
|
fun empty(
|
||||||
player: String = "",
|
player: String = "",
|
||||||
host: String = "",
|
host: String = "",
|
||||||
|
|
@ -167,11 +168,14 @@ fun NetworkScreen(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NetworkPage(
|
fun NetworkPage(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
viewModel: NetworkViewModel = koinViewModel(),
|
viewModel: NetworkViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
val snack = LocalSnackHost.current
|
val snack = LocalSnackHost.current
|
||||||
|
|
||||||
Surface {
|
Surface(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
BlurContent(
|
BlurContent(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
controller = viewModel.blurController,
|
controller = viewModel.blurController,
|
||||||
|
|
@ -267,6 +271,7 @@ private fun NetworkContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
TextButton(
|
TextButton(
|
||||||
|
modifier = Modifier.align(alignment = Alignment.End),
|
||||||
enabled = player.value.enableActions,
|
enabled = player.value.enableActions,
|
||||||
onClick = onConnect,
|
onClick = onConnect,
|
||||||
) {
|
) {
|
||||||
|
|
@ -274,6 +279,7 @@ private fun NetworkContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
TextButton(
|
TextButton(
|
||||||
|
modifier = Modifier.align(alignment = Alignment.End),
|
||||||
enabled = player.value.enableCancel,
|
enabled = player.value.enableCancel,
|
||||||
onClick = onDisconnect,
|
onClick = onDisconnect,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,11 @@ class NetworkViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onPortChange(port: String) {
|
fun onPortChange(port: String) {
|
||||||
this.portFlow.value = port.toIntOrNull() ?: settings.port
|
portFlow.value = port.toIntOrNull() ?: settings.port
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onHostChange(host: String) {
|
fun onHostChange(host: String) {
|
||||||
this.hostFlow.value = host
|
hostFlow.value = host
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connect() {
|
fun connect() {
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,16 @@ class RollViewModel(
|
||||||
rollValue = roll,
|
rollValue = roll,
|
||||||
rollSuccessLimit = rollStep?.success?.last,
|
rollSuccessLimit = rollStep?.success?.last,
|
||||||
resultLabel = success,
|
resultLabel = success,
|
||||||
|
critical = rollStep?.let {
|
||||||
|
when (roll) {
|
||||||
|
in it.criticalSuccess -> RollMessage.Critical.CRITICAL_SUCCESS
|
||||||
|
in it.specialSuccess -> RollMessage.Critical.SPECIAL_SUCCESS
|
||||||
|
in it.success -> RollMessage.Critical.SUCCESS
|
||||||
|
in it.failure -> RollMessage.Critical.FAILURE
|
||||||
|
in it.criticalFailure -> RollMessage.Critical.CRITICAL_FAILURE
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
networkRepository.share(
|
networkRepository.share(
|
||||||
payload = payload,
|
payload = payload,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.Scaffold
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.TopAppBar
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsScreen() {
|
||||||
|
val screen = LocalScreenController.current
|
||||||
|
|
||||||
|
Surface {
|
||||||
|
SettingsContent(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
onBack = {
|
||||||
|
screen.popBackStack()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SettingsContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
modifier = modifier,
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = onBack,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = { paddingValues ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,10 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import com.pixelized.desktop.lwa.ui.theme.color.LwaColorTheme
|
import com.pixelized.desktop.lwa.ui.theme.color.LwaColors
|
||||||
import com.pixelized.desktop.lwa.ui.theme.color.darkLwaColorTheme
|
import com.pixelized.desktop.lwa.ui.theme.color.darkLwaColorTheme
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.typography.lwaTypography
|
||||||
|
|
||||||
val LocalLwaTheme = compositionLocalOf<LwaTheme> {
|
val LocalLwaTheme = compositionLocalOf<LwaTheme> {
|
||||||
error("Local Snack Controller is not yet ready")
|
error("Local Snack Controller is not yet ready")
|
||||||
|
|
@ -20,17 +22,21 @@ val MaterialTheme.lwa: LwaTheme
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class LwaTheme(
|
data class LwaTheme(
|
||||||
val colorScheme: LwaColorTheme,
|
val colorScheme: LwaColors,
|
||||||
|
val typography: LwaTypography,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LwaTheme(
|
fun LwaTheme(
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val lwaColorTheme = darkLwaColorTheme()
|
val lwaColors = darkLwaColorTheme()
|
||||||
|
val lwaTypography = lwaTypography(colors = lwaColors)
|
||||||
|
|
||||||
val theme = remember {
|
val theme = remember {
|
||||||
LwaTheme(
|
LwaTheme(
|
||||||
colorScheme = lwaColorTheme,
|
colorScheme = lwaColors,
|
||||||
|
typography = lwaTypography,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +44,7 @@ fun LwaTheme(
|
||||||
LocalLwaTheme provides theme
|
LocalLwaTheme provides theme
|
||||||
) {
|
) {
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colors = lwaColorTheme.base,
|
colors = lwaColors.base,
|
||||||
typography = MaterialTheme.typography,
|
typography = MaterialTheme.typography,
|
||||||
shapes = MaterialTheme.shapes,
|
shapes = MaterialTheme.shapes,
|
||||||
content = content,
|
content = content,
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,10 @@ import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
object LwaColorPalette {
|
object LwaColorPalette {
|
||||||
val DefaultScrimColor = Color.Black.copy(alpha = 0.4f)
|
val DefaultScrimColor = Color.Black.copy(alpha = 0.4f)
|
||||||
|
|
||||||
|
val Orange400 = Color(0xFFFFA726)
|
||||||
|
val Red400 = Color(0xFFFF7043)
|
||||||
|
val LightGreen400 = Color(0xFF9CCC65)
|
||||||
|
val Green400 = Color(0xFF66BB6A)
|
||||||
|
val Teal400 = Color(0xFF26A69A)
|
||||||
}
|
}
|
||||||
|
|
@ -13,15 +13,29 @@ import androidx.compose.ui.unit.dp
|
||||||
import kotlin.math.ln
|
import kotlin.math.ln
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class LwaColorTheme(
|
data class LwaColors(
|
||||||
val base: Colors,
|
val base: Colors,
|
||||||
val elevated: Elevated,
|
val elevated: Elevated,
|
||||||
val portraitBackgroundBrush: Brush,
|
val portraitBackgroundBrush: Brush,
|
||||||
|
val chat: Chat,
|
||||||
) {
|
) {
|
||||||
@Stable
|
@Stable
|
||||||
data class Elevated(
|
data class Elevated(
|
||||||
val base1dp: Color,
|
val base1dp: Color,
|
||||||
val base2dp: Color,
|
val base2dp: Color,
|
||||||
|
val base3dp: Color,
|
||||||
|
val base4dp: Color,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class Chat(
|
||||||
|
val timestamp: Color,
|
||||||
|
val text: Color,
|
||||||
|
val criticalSuccess: Color,
|
||||||
|
val spacialSuccess: Color,
|
||||||
|
val success: Color,
|
||||||
|
val failure: Color,
|
||||||
|
val criticalFailure: Color,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +43,7 @@ data class LwaColorTheme(
|
||||||
@Stable
|
@Stable
|
||||||
fun darkLwaColorTheme(
|
fun darkLwaColorTheme(
|
||||||
base: Colors = darkColors(),
|
base: Colors = darkColors(),
|
||||||
elevated: LwaColorTheme.Elevated = LwaColorTheme.Elevated(
|
elevated: LwaColors.Elevated = LwaColors.Elevated(
|
||||||
base1dp = base.calculateElevatedColor(
|
base1dp = base.calculateElevatedColor(
|
||||||
color = base.surface,
|
color = base.surface,
|
||||||
onColor = base.onSurface,
|
onColor = base.onSurface,
|
||||||
|
|
@ -40,6 +54,16 @@ fun darkLwaColorTheme(
|
||||||
onColor = base.onSurface,
|
onColor = base.onSurface,
|
||||||
elevation = 2.dp,
|
elevation = 2.dp,
|
||||||
),
|
),
|
||||||
|
base3dp = base.calculateElevatedColor(
|
||||||
|
color = base.surface,
|
||||||
|
onColor = base.onSurface,
|
||||||
|
elevation = 3.dp,
|
||||||
|
),
|
||||||
|
base4dp = base.calculateElevatedColor(
|
||||||
|
color = base.surface,
|
||||||
|
onColor = base.onSurface,
|
||||||
|
elevation = 4.dp,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
portraitBackgroundBrush: Brush = Brush.verticalGradient(
|
portraitBackgroundBrush: Brush = Brush.verticalGradient(
|
||||||
listOf(
|
listOf(
|
||||||
|
|
@ -49,11 +73,21 @@ fun darkLwaColorTheme(
|
||||||
elevated.base1dp.copy(alpha = 0.5f),
|
elevated.base1dp.copy(alpha = 0.5f),
|
||||||
elevated.base1dp.copy(alpha = 0.8f),
|
elevated.base1dp.copy(alpha = 0.8f),
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
): LwaColorTheme = LwaColorTheme(
|
chat: LwaColors.Chat = LwaColors.Chat(
|
||||||
|
timestamp = base.secondary,
|
||||||
|
text = base.onSurface.copy(alpha = 0.7f),
|
||||||
|
criticalSuccess = LwaColorPalette.Teal400,
|
||||||
|
spacialSuccess = LwaColorPalette.Green400,
|
||||||
|
success = LwaColorPalette.LightGreen400,
|
||||||
|
failure = LwaColorPalette.Orange400,
|
||||||
|
criticalFailure = LwaColorPalette.Red400,
|
||||||
|
),
|
||||||
|
): LwaColors = LwaColors(
|
||||||
base = base,
|
base = base,
|
||||||
elevated = elevated,
|
elevated = elevated,
|
||||||
portraitBackgroundBrush = portraitBackgroundBrush,
|
portraitBackgroundBrush = portraitBackgroundBrush,
|
||||||
|
chat = chat,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.theme.typography
|
||||||
|
|
||||||
|
import androidx.compose.material.Typography
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.color.LwaColors
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.consola_mono_bold
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.consola_mono_book
|
||||||
|
import org.jetbrains.compose.resources.Font
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class LwaTypography(
|
||||||
|
val base: Typography,
|
||||||
|
val chat: Chat,
|
||||||
|
) {
|
||||||
|
@Stable
|
||||||
|
data class Chat(
|
||||||
|
val timestamp: TextStyle,
|
||||||
|
val text: TextStyle,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun ConsolaFontFamily() = FontFamily(
|
||||||
|
Font(resource = Res.font.consola_mono_book, weight = FontWeight.Normal),
|
||||||
|
Font(resource = Res.font.consola_mono_bold, weight = FontWeight.Bold),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun lwaTypography(
|
||||||
|
base: Typography = Typography(),
|
||||||
|
colors: LwaColors,
|
||||||
|
): LwaTypography {
|
||||||
|
val jack = ConsolaFontFamily()
|
||||||
|
|
||||||
|
return remember(
|
||||||
|
jack,
|
||||||
|
) {
|
||||||
|
LwaTypography(
|
||||||
|
base = base,
|
||||||
|
chat = LwaTypography.Chat(
|
||||||
|
timestamp = base.body1.copy(
|
||||||
|
fontFamily = jack,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.sp,
|
||||||
|
color = colors.chat.timestamp,
|
||||||
|
),
|
||||||
|
text = base.body1.copy(
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.4.sp,
|
||||||
|
color = colors.chat.text,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,4 +10,13 @@ data class RollMessage(
|
||||||
val rollDifficulty: String? = null,
|
val rollDifficulty: String? = null,
|
||||||
val rollValue: Int,
|
val rollValue: Int,
|
||||||
val rollSuccessLimit: Int? = null,
|
val rollSuccessLimit: Int? = null,
|
||||||
) : MessagePayload
|
val critical: Critical?,
|
||||||
|
) : MessagePayload {
|
||||||
|
enum class Critical {
|
||||||
|
CRITICAL_SUCCESS,
|
||||||
|
SPECIAL_SUCCESS,
|
||||||
|
SUCCESS,
|
||||||
|
FAILURE,
|
||||||
|
CRITICAL_FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue