diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d10.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d10.webp new file mode 100644 index 0000000..343d378 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d10.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d100.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d100.webp new file mode 100644 index 0000000..a09d047 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d100.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d12.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d12.webp new file mode 100644 index 0000000..5515d20 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d12.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d20.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d20.webp new file mode 100644 index 0000000..216de6b Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d20.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d4.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d4.webp new file mode 100644 index 0000000..5914081 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d4.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d6.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d6.webp new file mode 100644 index 0000000..579cc92 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d6.webp differ diff --git a/composeApp/src/commonMain/composeResources/drawable/icon_d8.webp b/composeApp/src/commonMain/composeResources/drawable/icon_d8.webp new file mode 100644 index 0000000..184096d Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/icon_d8.webp differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Bold.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Bold.ttf new file mode 100644 index 0000000..8069fa6 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Bold.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-BoldItalic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-BoldItalic.ttf new file mode 100644 index 0000000..44155b1 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-BoldItalic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraBold.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraBold.ttf new file mode 100644 index 0000000..8305c81 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraBold.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraBoldItalic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraBoldItalic.ttf new file mode 100644 index 0000000..852be33 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraBoldItalic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraLight.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraLight.ttf new file mode 100644 index 0000000..ea25d0c Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraLight.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraLightItalic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraLightItalic.ttf new file mode 100644 index 0000000..139c86e Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-ExtraLightItalic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Italic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Italic.ttf new file mode 100644 index 0000000..3541e33 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Italic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Light.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Light.ttf new file mode 100644 index 0000000..1475380 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Light.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-LightItalic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-LightItalic.ttf new file mode 100644 index 0000000..c6eb252 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-LightItalic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Medium.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Medium.ttf new file mode 100644 index 0000000..ef77aec Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Medium.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-MediumItalic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-MediumItalic.ttf new file mode 100644 index 0000000..dd76502 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-MediumItalic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Regular.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Regular.ttf new file mode 100644 index 0000000..157d62e Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-Regular.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-SemiBold.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-SemiBold.ttf new file mode 100644 index 0000000..3dbc00e Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-SemiBold.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-SemiBoldItalic.ttf b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-SemiBoldItalic.ttf new file mode 100644 index 0000000..5d06e94 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/AtkinsonHyperlegibleMono-SemiBoldItalic.ttf differ diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index f9e12cf..c070d7e 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -57,7 +57,6 @@ Spécial Critique - Ressources Table de jeu Règles de jeu Carte du monde diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbar.kt index e07a816..b95586e 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbar.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbar.kt @@ -13,13 +13,14 @@ import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.Stable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import com.pixelized.desktop.lwa.LocalWindowController import com.pixelized.desktop.lwa.repository.network.NetworkRepository @@ -34,12 +35,17 @@ import lwacharactersheet.composeapp.generated.resources.ic_link_24dp import lwacharactersheet.composeapp.generated.resources.ic_settings_24dp import lwacharactersheet.composeapp.generated.resources.ic_wifi_24dp import lwacharactersheet.composeapp.generated.resources.ic_wifi_off_24dp -import lwacharactersheet.composeapp.generated.resources.resources__content_descriptions import lwacharactersheet.composeapp.generated.resources.settings__title import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel +@Stable +data class MenuState( + val isResourcesMenuOpen: MutableState = mutableStateOf(false), + val isNetworkMenuOpen: MutableState = mutableStateOf(false), +) + @Composable fun CampaignToolbar( viewModel: CampaignToolbarViewModel = koinViewModel(), @@ -47,8 +53,7 @@ fun CampaignToolbar( val windows = LocalWindowController.current val screen = LocalScreenController.current - val isResourcesMenuOpen = remember { mutableStateOf(false) } - val isNetworkMenuOpen = remember { mutableStateOf(false) } + val menuState = remember { MenuState() } val title = viewModel.title.collectAsState() val status = viewModel.status.collectAsState() @@ -58,25 +63,24 @@ fun CampaignToolbar( title = title, status = status, isAdmin = isAdmin, - isNetworkMenuOpen = isNetworkMenuOpen, - isResourcesMenuOpen = isResourcesMenuOpen, + state = menuState, onAdmin = { windows.navigateToGameMasterWindow() }, onNetwork = { - isNetworkMenuOpen.value = true + menuState.isNetworkMenuOpen.value = true }, onResources = { - isResourcesMenuOpen.value = true + menuState.isResourcesMenuOpen.value = true }, onSettings = { screen.navigateToSettings() }, onDismissNetworkMenu = { - isNetworkMenuOpen.value = false + menuState.isNetworkMenuOpen.value = false }, onDismissResourcesMenu = { - isResourcesMenuOpen.value = false + menuState.isResourcesMenuOpen.value = false }, ) } @@ -87,8 +91,7 @@ private fun CampaignToolbarContent( title: State, status: State, isAdmin: State, - isNetworkMenuOpen: State, - isResourcesMenuOpen: State, + state: MenuState, onAdmin: () -> Unit, onNetwork: () -> Unit, onResources: () -> Unit, @@ -124,7 +127,16 @@ private fun CampaignToolbarContent( Icon( painter = painterResource(Res.drawable.ic_link_24dp), tint = MaterialTheme.colors.primary, - contentDescription = stringResource(Res.string.resources__content_descriptions), + contentDescription = null, + ) + DropdownMenu( + expanded = state.isResourcesMenuOpen.value, + onDismissRequest = onDismissResourcesMenu, + content = { + ResourcesMenu( + modifier = Modifier.width(384.dp + 96.dp), + ) + }, ) } IconButton( @@ -143,6 +155,15 @@ private fun CampaignToolbarContent( }, contentDescription = null, ) + DropdownMenu( + expanded = state.isNetworkMenuOpen.value, + onDismissRequest = onDismissNetworkMenu, + content = { + NetworkMenu( + modifier = Modifier.size(384.dp + 96.dp, 240.dp), + ) + }, + ) } IconButton( onClick = onSettings, @@ -153,26 +174,6 @@ private fun CampaignToolbarContent( contentDescription = stringResource(Res.string.settings__title), ) } - DropdownMenu( - offset = remember { DpOffset(x = -(48.dp + 8.dp), y = 8.dp) }, - expanded = isNetworkMenuOpen.value, - onDismissRequest = onDismissNetworkMenu, - content = { - NetworkMenu( - modifier = Modifier.size(384.dp + 96.dp, 240.dp), - ) - }, - ) - DropdownMenu( - offset = remember { DpOffset(x = -(48.dp + 8.dp), y = 8.dp) }, - expanded = isResourcesMenuOpen.value, - onDismissRequest = onDismissResourcesMenu, - content = { - ResourcesMenu( - modifier = Modifier.width(384.dp + 96.dp), - ) - }, - ) }, ) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/dice/DiceMenu.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/dice/DiceMenu.kt new file mode 100644 index 0000000..3c68c61 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/dice/DiceMenu.kt @@ -0,0 +1,353 @@ +package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.dice + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.ContentTransform +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.PointerMatcher +import androidx.compose.foundation.layout.Arrangement +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.layout.size +import androidx.compose.foundation.onClick +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.input.pointer.PointerButton +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors +import com.pixelized.desktop.lwa.ui.theme.lwa +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.icon_d10 +import lwacharactersheet.composeapp.generated.resources.icon_d100 +import lwacharactersheet.composeapp.generated.resources.icon_d12 +import lwacharactersheet.composeapp.generated.resources.icon_d20 +import lwacharactersheet.composeapp.generated.resources.icon_d4 +import lwacharactersheet.composeapp.generated.resources.icon_d6 +import lwacharactersheet.composeapp.generated.resources.icon_d8 +import org.jetbrains.compose.resources.painterResource +import java.util.UUID +import kotlin.math.max +import kotlin.random.Random +import kotlin.random.nextInt + +@Stable +data class DiceMenuUio( + val d4: Int = 0, + val d6: Int = 0, + val d8: Int = 0, + val d10: Int = 0, + val d12: Int = 0, + val d20: Int = 0, + val d100: Int = 1, + val result: Result? = null, +) { + @Stable + data class Result( + val id: String, + val value: Int, + ) +} + +@Stable +data object DiceMenuDefault { + @Stable + val size: Dp = 32.dp + + @Stable + val spacings: DpSize = DpSize(width = 2.dp, height = 8.dp) + + @Stable + val paddings = PaddingValues(horizontal = 8.dp) +} + +@Composable +fun DiceMenu( + modifier: Modifier = Modifier, +) { + val dices = remember { + mutableStateOf( + DiceMenuUio() + ) + } + DiceMenuContent( + modifier = modifier, + dices = dices, + onAddD4 = { + dices.value = dices.value.copy(d4 = max(0, dices.value.d4 + 1)) + }, + onRemoveD4 = { + dices.value = dices.value.copy(d4 = max(0, dices.value.d4 - 1)) + }, + onAddD6 = { + dices.value = dices.value.copy(d6 = max(0, dices.value.d6 + 1)) + }, + onRemoveD6 = { + dices.value = dices.value.copy(d6 = max(0, dices.value.d6 - 1)) + }, + onAddD8 = { + dices.value = dices.value.copy(d8 = max(0, dices.value.d8 + 1)) + }, + onRemoveD8 = { + dices.value = dices.value.copy(d8 = max(0, dices.value.d8 - 1)) + }, + onAddD10 = { + dices.value = dices.value.copy(d10 = max(0, dices.value.d10 + 1)) + }, + onRemoveD10 = { + dices.value = dices.value.copy(d10 = max(0, dices.value.d10 - 1)) + }, + onAddD12 = { + dices.value = dices.value.copy(d12 = max(0, dices.value.d12 + 1)) + }, + onRemoveD12 = { + dices.value = dices.value.copy(d12 = max(0, dices.value.d12 - 1)) + }, + onAddD20 = { + dices.value = dices.value.copy(d20 = max(0, dices.value.d20 + 1)) + }, + onRemoveD20 = { + dices.value = dices.value.copy(d20 = max(0, dices.value.d20 - 1)) + }, + onAddD100 = { + dices.value = dices.value.copy(d100 = max(0, dices.value.d100 + 1)) + }, + onRemoveD100 = { + dices.value = dices.value.copy(d100 = max(0, dices.value.d100 - 1)) + }, + onRoll = { + dices.value.let { dice -> + val d4 = (0 until dice.d4) + .also { print("${it.last + 1}d4:(") } + .sumOf { Random.nextInt(range = 1..4).also { print("$it,") } } + .also { print(")+") } + val d6 = (0 until dice.d6) + .also { print("${it.last + 1}:d6(") } + .sumOf { Random.nextInt(range = 1..6).also { print("$it,") } } + .also { print(")+") } + val d8 = (0 until dice.d8) + .also { print("${it.last + 1}d8:(") } + .sumOf { Random.nextInt(range = 1..8).also { print("$it,") } } + .also { print(")+") } + val d10 = (0 until dice.d10) + .also { print("${it.last + 1}d10:(") } + .sumOf { Random.nextInt(range = 1..10).also { print("$it,") } } + .also { print(")+") } + val d12 = (0 until dice.d12) + .also { print("${it.last + 1}d12:(") } + .sumOf { Random.nextInt(range = 1..12).also { print("$it,") } } + .also { print(")+") } + val d20 = (0 until dice.d20) + .also { print("${it.last + 1}d20:(") } + .sumOf { Random.nextInt(range = 1..20).also { print("$it,") } } + .also { print(")+") } + val d100 = (0 until dice.d100) + .also { print("${it.last + 1}d100:(") } + .sumOf { Random.nextInt(range = 1..100).also { print("$it,") } } + .also { println(")") } + dices.value = dices.value.copy( + result = DiceMenuUio.Result( + id = UUID.randomUUID().toString(), + value = d4 + d6 + d8 + d10 + d12 + d20 + d100, + ), + ) + } + } + ) +} + +@Composable +private fun DiceMenuContent( + modifier: Modifier = Modifier, + size: Dp = DiceMenuDefault.size, + spacings: DpSize = DiceMenuDefault.spacings, + paddingValues: PaddingValues = DiceMenuDefault.paddings, + dices: State, + onAddD4: () -> Unit, + onRemoveD4: () -> Unit, + onAddD6: () -> Unit, + onRemoveD6: () -> Unit, + onAddD8: () -> Unit, + onRemoveD8: () -> Unit, + onAddD10: () -> Unit, + onRemoveD10: () -> Unit, + onAddD12: () -> Unit, + onRemoveD12: () -> Unit, + onAddD20: () -> Unit, + onRemoveD20: () -> Unit, + onAddD100: () -> Unit, + onRemoveD100: () -> Unit, + onRoll: () -> Unit, +) { + Column( + modifier = Modifier + .padding(paddingValues = paddingValues) + .then(other = modifier), + verticalArrangement = Arrangement.spacedBy(space = spacings.height), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Dice( + painter = painterResource(Res.drawable.icon_d4), + size = size, + spacings = spacings.width, + value = dices.value.d4, + onAdd = onAddD4, + onRemove = onRemoveD4, + ) + Dice( + painter = painterResource(Res.drawable.icon_d6), + size = size, + spacings = spacings.width, + value = dices.value.d6, + onAdd = onAddD6, + onRemove = onRemoveD6, + ) + Dice( + painter = painterResource(Res.drawable.icon_d8), + size = size, + spacings = spacings.width, + value = dices.value.d8, + onAdd = onAddD8, + onRemove = onRemoveD8, + ) + Dice( + painter = painterResource(Res.drawable.icon_d10), + size = size, + spacings = spacings.width, + value = dices.value.d10, + onAdd = onAddD10, + onRemove = onRemoveD10, + ) + Dice( + painter = painterResource(Res.drawable.icon_d12), + size = size, + spacings = spacings.width, + value = dices.value.d12, + onAdd = onAddD12, + onRemove = onRemoveD12, + ) + Dice( + painter = painterResource(Res.drawable.icon_d20), + size = size, + spacings = spacings.width, + value = dices.value.d20, + onAdd = onAddD20, + onRemove = onRemoveD20, + ) + Dice( + painter = painterResource(Res.drawable.icon_d100), + size = size, + spacings = spacings.width, + value = dices.value.d100, + postSign = "=", + onAdd = onAddD100, + onRemove = onRemoveD100, + ) + AnimatedContent( + modifier = Modifier.padding(top = spacings.height), + targetState = dices.value.result, + transitionSpec = { + val enter = fadeIn() + slideInVertically { -16 } + val exit = fadeOut() + slideOutVertically { 16 } + enter togetherWith exit using SizeTransform(clip = false) + }, + ) { + Text( + modifier = Modifier.fillMaxWidth(), + style = MaterialTheme.lwa.typography.dice.result, + textAlign = TextAlign.Center, + text = "${it?.value ?: ""}" + ) + } + TextButton( + modifier = Modifier.align(alignment = Alignment.CenterHorizontally), + colors = LwaButtonColors(), + onClick = onRoll, + ) { + Text( + text = "Roll" + ) + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun Dice( + modifier: Modifier = Modifier, + size: Dp, + spacings: Dp, + painter: Painter, + value: Int, + postSign: String = "+", + onAdd: () -> Unit, + onRemove: () -> Unit, +) { + Row( + modifier = Modifier + .onClick( + matcher = PointerMatcher.mouse(PointerButton.Primary), + onClick = onAdd, + ) + .onClick( + matcher = PointerMatcher.mouse(PointerButton.Secondary), + onClick = onRemove, + ) + .then( + other = modifier, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = spacings), + ) { + Image( + modifier = Modifier.size(size), + painter = painter, + contentDescription = null, + ) + Text( + style = MaterialTheme.lwa.typography.dice.dice, + text = "×" + ) + AnimatedContent( + targetState = value, + transitionSpec = { contentTransform() } + ) { + Text( + style = MaterialTheme.lwa.typography.dice.dice, + text = "$it" + ) + } + Text( + style = MaterialTheme.lwa.typography.dice.dice, + text = postSign, + ) + } +} + +private fun AnimatedContentTransitionScope.contentTransform(): ContentTransform { + val sign = if (initialState < targetState) 1 else -1 + val enter = fadeIn() + slideInVertically { -16 * sign } + val exit = fadeOut() + slideOutVertically { 16 * sign } + return enter togetherWith exit using SizeTransform(clip = false) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/links/ResourcesDialog.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/links/ResourcesMenu.kt similarity index 94% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/links/ResourcesDialog.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/links/ResourcesMenu.kt index 2defca4..2710c1d 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/links/ResourcesDialog.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/links/ResourcesMenu.kt @@ -1,17 +1,14 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.links import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.clickable 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.layout.size -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -87,7 +84,9 @@ private fun ResourcesContent( spacing: Dp = ResourcesDialogDefault.spacing, ) { Column( - modifier = Modifier.padding(paddingValues = padding).then(other = modifier), + modifier = Modifier + .padding(paddingValues = padding) + .then(other = modifier), verticalArrangement = Arrangement.spacedBy(space = spacing), ) { dialog.resources.forEach { resource -> 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 85babfa..a643297 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,5 +1,6 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster +import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -8,8 +9,12 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.DropdownMenu +import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Surface @@ -19,6 +24,8 @@ import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -39,6 +46,7 @@ import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.nav 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.campaign.toolbar.dice.DiceMenu import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMTab import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMTabUio import com.pixelized.desktop.lwa.ui.theme.color.component.LwaSwitchColors @@ -47,6 +55,8 @@ import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.game_master__action import lwacharactersheet.composeapp.generated.resources.game_master__title import lwacharactersheet.composeapp.generated.resources.app_version +import lwacharactersheet.composeapp.generated.resources.icon_d100 +import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel @@ -56,10 +66,12 @@ fun GameMasterScreen( controller: NavHostController = LocalGMScreenController.current, ) { val gameMaster = gameMasterViewModel.isGameMaster.collectAsState() + val isDiceMenuOpen = remember { mutableStateOf(false) } GameMasterContent( modifier = Modifier.fillMaxSize(), controller = controller, + isDiceMenuOpen = isDiceMenuOpen, gameMaster = gameMaster, onGameMaster = gameMasterViewModel::onGameMaster, onTab = { @@ -70,6 +82,9 @@ fun GameMasterScreen( GMTabUio.Objects -> controller.navigateToGameMasterObjectPage() } }, + onDice = { + isDiceMenuOpen.value = it + } ) } @@ -77,7 +92,9 @@ fun GameMasterScreen( private fun GameMasterContent( modifier: Modifier = Modifier, controller: NavHostController = rememberNavController(), + isDiceMenuOpen: State, gameMaster: State, + onDice: (Boolean) -> Unit, onGameMaster: (Boolean) -> Unit, onTab: (GMTabUio) -> Unit, ) { @@ -121,6 +138,24 @@ private fun GameMasterContent( onCheckedChange = null, ) } + IconButton( + onClick = { onDice(true) }, + ) { + Image( + modifier = Modifier.size(32.dp), + painter = painterResource(Res.drawable.icon_d100), + contentDescription = null, + ) + DropdownMenu( + expanded = isDiceMenuOpen.value, + onDismissRequest = { onDice(false) }, + content = { + DiceMenu( + modifier = Modifier.widthIn(min = 92.dp), + ) + }, + ) + } } ) }, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt index 740fa28..1c6cf65 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/typography/LwaTypography.kt @@ -18,6 +18,20 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp import com.pixelized.desktop.lwa.ui.theme.color.LwaColors import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography.Settings +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_Bold +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_BoldItalic +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_ExtraBold +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_ExtraBoldItalic +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_ExtraLight +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_ExtraLightItalic +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_Italic +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_Light +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_LightItalic +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_Medium +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_MediumItalic +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_Regular +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_SemiBold +import lwacharactersheet.composeapp.generated.resources.AtkinsonHyperlegibleMono_SemiBoldItalic import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.consola_mono_bold import lwacharactersheet.composeapp.generated.resources.consola_mono_book @@ -30,6 +44,7 @@ data class LwaTypography( val settings: Settings, val portrait: Portrait, val inventory: Inventory, + val dice: Dice, ) { @Stable data class Chat( @@ -57,6 +72,12 @@ data class LwaTypography( val countInline: TextStyle, val label: TextStyle, ) + + @Stable + data class Dice( + val dice: TextStyle, + val result: TextStyle, + ) } @Composable @@ -66,6 +87,81 @@ fun ConsolaFontFamily() = FontFamily( Font(resource = Res.font.consola_mono_bold, weight = FontWeight.Bold), ) +@Composable +@Stable +fun AtkinsonHyperLigibleMonoFontFamily() = FontFamily( + Font( + resource = Res.font.AtkinsonHyperlegibleMono_ExtraLight, + weight = FontWeight.ExtraLight, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_Light, + weight = FontWeight.Light, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_Regular, + weight = FontWeight.Normal, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_Medium, + weight = FontWeight.Medium, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_SemiBold, + weight = FontWeight.SemiBold, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_Bold, + weight = FontWeight.Bold, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_ExtraBold, + weight = FontWeight.ExtraBold, + style = FontStyle.Normal + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_ExtraLightItalic, + weight = FontWeight.ExtraLight, + style = FontStyle.Italic + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_LightItalic, + weight = FontWeight.Light, + style = FontStyle.Italic + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_Italic, + weight = FontWeight.Normal, + style = FontStyle.Italic + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_MediumItalic, + weight = FontWeight.Medium, + style = FontStyle.Italic + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_SemiBoldItalic, + weight = FontWeight.SemiBold, + style = FontStyle.Italic + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_BoldItalic, + weight = FontWeight.Bold, + style = FontStyle.Italic + ), + Font( + resource = Res.font.AtkinsonHyperlegibleMono_ExtraBoldItalic, + weight = FontWeight.ExtraBold, + style = FontStyle.Italic + ), +) + @Composable @Stable fun lwaTypography( @@ -73,6 +169,7 @@ fun lwaTypography( colors: LwaColors, ): LwaTypography { val consolaFontFamily = ConsolaFontFamily() + val atkinsonHyperLigibleMonoFontFamily = AtkinsonHyperLigibleMonoFontFamily() return remember(consolaFontFamily) { LwaTypography( @@ -146,6 +243,16 @@ fun lwaTypography( label = base.body1.copy( fontWeight = FontWeight.Normal, ), + ), + dice = LwaTypography.Dice( + dice = base.h6.copy( + fontFamily = atkinsonHyperLigibleMonoFontFamily, + color = colors.base.onSurface, + ), + result = base.h4.copy( + fontFamily = atkinsonHyperLigibleMonoFontFamily, + color = colors.base.onSurface, + ), ) ) }