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