From d648b8a05e2138c2f311b864132d14a541fb9dc5 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Wed, 26 Nov 2025 18:03:09 +0100 Subject: [PATCH] Add MapView into the App --- .../drawable/ic_frame_bug_24dp.xml | 9 + .../composeResources/drawable/ic_map_24dp.xml | 9 + .../com/pixelized/desktop/lwa/Module.kt | 4 +- .../desktop/lwa/ui/composable/scene/Camera.kt | 177 --------------- .../lwa/ui/composable/scene/DahomeMap.kt | 177 --------------- .../desktop/lwa/ui/composable/scene/Scene.kt | 189 +--------------- .../lwa/ui/composable/scene/camera/Camera.kt | 97 +++++++++ .../scene/camera/Modifier+onCameraControl.kt | 75 +++++++ .../lwa/ui/composable/scene/cursor/Cursor.kt | 24 ++ .../scene/cursor/Modifier+drawCursor.kt | 26 +++ .../scene/cursor/Modifier+onCursorControl.kt | 26 +++ .../scene/debug/CameraDebugPanel.kt | 82 +++++++ .../{Cursor.kt => debug/CursorDebugPanel.kt} | 53 ++--- .../scene/{ => debug}/SceneDebugPanel.kt | 111 ++++------ .../scene/drawables/SceneElement.kt | 8 +- .../composable/scene/drawables/SceneLayer.kt | 10 +- .../ui/composable/scene/fogOfWar/FogOfWar.kt | 44 ++++ .../scene/fogOfWar/Modifier+drawFogOfWar.kt | 53 +++++ .../Modifier+onFogOfWarControl.kt} | 28 +-- ...{Offset+Camera.kt => Offset+Coordinate.kt} | 2 +- .../lwa/ui/navigation/window/WindowNavHost.kt | 2 +- .../window/destination/GameMasterWindow.kt | 4 +- .../lwa/ui/screen/campaign/CampaignScreen.kt | 10 +- .../{text => chatbox}/CampaignChat.kt | 20 +- .../CampaignChatViewModel.kt | 2 +- .../{text => chatbox}/TextMessageFactory.kt | 12 +- .../messages/CharacteristicTextMessage.kt | 2 +- .../messages/DiminishedTextMessage.kt | 2 +- .../messages/PurseTextMessage.kt | 3 +- .../messages/RollTextMessage.kt | 2 +- .../{text => chatbox}/messages/TextMessage.kt | 2 +- .../lwa/ui/screen/campaign/map/DahomeMap.kt | 205 ++++++++++++++++++ .../campaign/toolbar/CampaignToolbar.kt | 76 ++++--- .../toolbar/CampaignToolbarViewModel.kt | 48 +++- .../lwa/ui/theme/typography/LwaTypography.kt | 8 +- gradle/libs.versions.toml | 10 + 36 files changed, 867 insertions(+), 745 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_frame_bug_24dp.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_map_24dp.xml delete mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Camera.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/DahomeMap.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Camera.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Modifier+onCameraControl.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Cursor.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+drawCursor.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+onCursorControl.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CameraDebugPanel.kt rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/{Cursor.kt => debug/CursorDebugPanel.kt} (59%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/{ => debug}/SceneDebugPanel.kt (58%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/FogOfWar.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+drawFogOfWar.kt rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/{FogOfWar.kt => fogOfWar/Modifier+onFogOfWarControl.kt} (74%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/{Offset+Camera.kt => Offset+Coordinate.kt} (94%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/CampaignChat.kt (89%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/CampaignChatViewModel.kt (97%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/TextMessageFactory.kt (94%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/messages/CharacteristicTextMessage.kt (96%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/messages/DiminishedTextMessage.kt (96%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/messages/PurseTextMessage.kt (97%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/messages/RollTextMessage.kt (98%) rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/{text => chatbox}/messages/TextMessage.kt (53%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/map/DahomeMap.kt diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_frame_bug_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_frame_bug_24dp.xml new file mode 100644 index 0000000..382f6bd --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_frame_bug_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_map_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_map_24dp.xml new file mode 100644 index 0000000..b9b1446 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_map_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt index 7a9467f..3e25c6c 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt @@ -41,8 +41,8 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.Characte import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonFactory import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.NpcRibbonViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbonViewModel -import com.pixelized.desktop.lwa.ui.screen.campaign.text.CampaignChatViewModel -import com.pixelized.desktop.lwa.ui.screen.campaign.text.TextMessageFactory +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.CampaignChatViewModel +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.TextMessageFactory import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbarViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.links.ResourcesViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.network.NetworkFactory diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Camera.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Camera.kt deleted file mode 100644 index 27d3733..0000000 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Camera.kt +++ /dev/null @@ -1,177 +0,0 @@ -package com.pixelized.desktop.lwa.ui.composable.scene - -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.VectorConverter -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.dp -import androidx.compose.ui.util.fastRoundToInt -import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneLayer -import com.pixelized.desktop.lwa.ui.theme.lwa -import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography - -@Stable -class Camera( - private val initialZoom: Float = 2f, - private val initialOffset: IntOffset = IntOffset.Zero, -) { - private var _zoom = Animatable( - initialValue = initialZoom, - typeConverter = Float.VectorConverter, - ) - val zoom: Float get() = _zoom.value - - private var _offset = Animatable( - initialValue = initialOffset, - typeConverter = IntOffset.VectorConverter, - ) - val offset: IntOffset by derivedStateOf { - _offset.value + IntOffset( - x = (_sceneSize.width - cameraSizeZoomed.width) / 2, - y = (_sceneSize.height - cameraSizeZoomed.height) / 2, - ) - } - - private var _sceneSize: IntSize by mutableStateOf(IntSize.Zero) - private var _cameraSize: IntSize by mutableStateOf(IntSize.Zero) - val cameraSize: IntSize get() = _cameraSize - val cameraSizeZoomed: IntSize by derivedStateOf { - IntSize( - width = (cameraSize.width * zoom).fastRoundToInt(), - height = (cameraSize.height * zoom).fastRoundToInt(), - ) - } - - fun changeSizes( - sceneSize: IntSize, - cameraSize: IntSize, - ) { - _cameraSize = cameraSize - _sceneSize = sceneSize - } - - suspend fun handlePanning( - delta: Offset, - snap: Boolean, - ) { - val value = _offset.value - IntOffset( - x = (delta.x * zoom).fastRoundToInt(), - y = (delta.y * zoom).fastRoundToInt(), - ) - when { - snap -> _offset.snapTo(targetValue = value) - else -> _offset.animateTo(targetValue = value) - } - } - - suspend fun handleZoom( - zoomIn: Boolean, - power: Float, - snap: Boolean = false, - ) { - val value = _zoom.value * when { - zoomIn -> 1f - power - else -> 1f + power - } - when { - snap -> _zoom.snapTo(targetValue = value) - else -> _zoom.animateTo(targetValue = value) - } - } - - suspend fun resetPosition( - snap: Boolean = false, - ) { - when (snap) { - true -> _offset.snapTo(targetValue = initialOffset) - else -> _offset.animateTo(targetValue = initialOffset) - } - } - - suspend fun resetZoom( - snap: Boolean = false, - ) { - when (snap) { - true -> _zoom.snapTo(targetValue = initialZoom) - else -> _zoom.animateTo(targetValue = initialZoom) - } - } -} - -@Composable -fun SceneCameraDebug( - modifier: Modifier = Modifier, - camera: Camera, - isOpen: Boolean = true, - style: LwaTypography.Debug = MaterialTheme.lwa.typography.debug, - padding: Dp = MaterialTheme.lwa.dimen.debug.offset, -) { - val isOpen = remember { mutableStateOf(isOpen) } - - Column( - modifier = Modifier - .clickable { isOpen.value = isOpen.value.not() } - .widthIn(min = MaterialTheme.lwa.dimen.debug.panelWidth) - .then(other = modifier), - verticalArrangement = Arrangement.spacedBy(space = 2.dp), - ) { - Text( - style = MaterialTheme.lwa.typography.debug.title, - color = MaterialTheme.lwa.colorScheme.base.primary, - text = "Camera", - ) - if (isOpen.value) { - Text( - modifier = Modifier.padding(start = padding), - style = MaterialTheme.lwa.typography.debug.propertyValue, - text = buildAnnotatedString { - withStyle(style.propertyId) { append("offset: ") } - append(camera.offset.toString()) - }, - ) - Text( - modifier = Modifier.padding(start = padding), - style = MaterialTheme.lwa.typography.debug.propertyValue, - text = buildAnnotatedString { - withStyle(style.propertyId) { append("size: ") } - append(camera.cameraSize.toString()) - }, - ) - Text( - modifier = Modifier.padding(start = padding), - style = MaterialTheme.lwa.typography.debug.propertyValue, - text = buildAnnotatedString { - withStyle(style.propertyId) { append("zoom: ") } - append(camera.zoom.toString()) - }, - ) - Text( - modifier = Modifier.padding(start = padding), - style = MaterialTheme.lwa.typography.debug.propertyValue, - text = buildAnnotatedString { - withStyle(style.propertyId) { append("size: ") } - append(camera.cameraSizeZoomed.toString()) - }, - ) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/DahomeMap.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/DahomeMap.kt deleted file mode 100644 index 349665e..0000000 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/DahomeMap.kt +++ /dev/null @@ -1,177 +0,0 @@ -package com.pixelized.desktop.lwa.ui.composable.scene - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.IntOffset -import com.pixelized.desktop.lwa.ui.screen.campaign.LocalCampaignLayoutScope -import kotlinx.coroutines.launch -import lwacharactersheet.composeapp.generated.resources.Res -import lwacharactersheet.composeapp.generated.resources.ic_center_focus_weak_24dp -import lwacharactersheet.composeapp.generated.resources.ic_foggy_24dp -import lwacharactersheet.composeapp.generated.resources.ic_foggy_filled_24dp -import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp -import lwacharactersheet.composeapp.generated.resources.ic_zoom_in_map_24dp -import lwacharactersheet.composeapp.generated.resources.ic_zoom_out_map_24dp -import lwacharactersheet.composeapp.generated.resources.icon_d100 -import lwacharactersheet.composeapp.generated.resources.image_dahome_maps -import lwacharactersheet.composeapp.generated.resources.image_dahome_regions -import org.jetbrains.compose.resources.painterResource - -@Composable -fun MapScene( - modifier: Modifier = Modifier, -) { - val campaign = LocalCampaignLayoutScope.current - val scope = rememberCoroutineScope() - val fogOfWarEdit = remember { mutableStateOf(true) } - - val map = rememberLayoutFromResource( - name = "Dahomé", - resource = Res.drawable.image_dahome_maps, - ) - val mapRegionOverlay = rememberLayoutFromResource( - name = "Région", - resource = Res.drawable.image_dahome_regions, - ) - val element1 = rememberElementFromResource( - name = "Group", - resource = Res.drawable.icon_d100, - offset = IntOffset( - x = 2128, - y = 1875, - ), - ) - val element2 = rememberElementFromResource( - name = "End", - resource = Res.drawable.icon_d100, - offset = IntOffset( - x = map.size.width, - y = map.size.height, - ) - ) - val element3 = rememberElementFromResource( - name = "Start", - resource = Res.drawable.icon_d100, - offset = IntOffset( - x = 0, - y = 0, - ) - ) - val scene = remember(map, mapRegionOverlay, element1, element2) { - Scene( - camera = Camera( - initialZoom = 1f, - initialOffset = IntOffset(x = -150, y = -120), - ), - fogOfWar = FogOfWar(), - layers = listOf( - map, - mapRegionOverlay, - ), - elements = listOf( - element1, - element2, - element3, - ), - ) - } - Scene( - modifier = modifier.onFogOfWarControl( - scope = scope, - enable = fogOfWarEdit.value, - fogOfWar = scene.fogOfWar, - camera = scene.camera, - ), - scene = scene, - ) { - Row( - modifier = Modifier - .align(alignment = Alignment.BottomStart) - .padding( - start = campaign.leftPanel.value.width, - bottom = campaign.chatOverlay.value.height, - ) - ) { - IconButton( - onClick = { - scope.launch { - scene.camera.handleZoom( - zoomIn = true, - power = 0.3f, - ) - } - } - ) { - Icon( - painter = painterResource(Res.drawable.ic_zoom_in_map_24dp), - contentDescription = null - ) - } - IconButton( - onClick = { - scope.launch { - scene.camera.handleZoom( - zoomIn = false, - power = 0.3f, - ) - } - } - ) { - Icon( - painter = painterResource(Res.drawable.ic_zoom_out_map_24dp), - contentDescription = null - ) - } - IconButton( - onClick = { - scope.launch { - scene.camera.resetPosition() - } - scope.launch { - scene.camera.resetZoom() - } - } - ) { - Icon( - painter = painterResource(Res.drawable.ic_center_focus_weak_24dp), - contentDescription = null - ) - } - IconButton( - onClick = { - scope.launch { - scene.layers.getOrNull(1)?.let { - it.alpha(alpha = if (it.alpha == 0f) 1f else 0f) - } - } - } - ) { - Icon( - painter = painterResource(Res.drawable.ic_visibility_24dp), - contentDescription = null - ) - } - IconButton( - onClick = { - fogOfWarEdit.value = fogOfWarEdit.value.not() - } - ) { - Icon( - painter = when (fogOfWarEdit.value) { - true -> painterResource(Res.drawable.ic_foggy_filled_24dp) - else -> painterResource(Res.drawable.ic_foggy_24dp) - }, - contentDescription = null - ) - } - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Scene.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Scene.kt index 21e6fb8..dace034 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Scene.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Scene.kt @@ -1,56 +1,31 @@ package com.pixelized.desktop.lwa.ui.composable.scene -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.padding -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable 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.draw.drawWithCache -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.CompositingStrategy -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.StrokeJoin import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.drawscope.scale -import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.pointer.PointerEvent -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.isAltPressed -import androidx.compose.ui.input.pointer.isCtrlPressed -import androidx.compose.ui.input.pointer.isPrimaryPressed -import androidx.compose.ui.input.pointer.isTertiaryPressed -import androidx.compose.ui.input.pointer.onPointerEvent -import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.toSize +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera +import com.pixelized.desktop.lwa.ui.composable.scene.camera.onCameraControl import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneElement import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneLayer -import com.pixelized.desktop.lwa.ui.composable.scene.utils.global import com.pixelized.desktop.lwa.ui.composable.scene.utils.local -import com.pixelized.desktop.lwa.ui.theme.lwa -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.imageResource import java.util.UUID -import kotlin.math.sign @Stable data class Scene( - val camera: Camera, - val fogOfWar: FogOfWar, val layers: List, val elements: List, ) { @@ -64,35 +39,21 @@ data class Scene( @Composable fun Scene( modifier: Modifier = Modifier, + camera: Camera, scene: Scene, content: @Composable BoxScope.() -> Unit, ) { val scope = rememberCoroutineScope() - val cursors = remember { - listOf( - Cursor() - ) - } Box( - modifier = modifier + modifier = Modifier .graphicsLayer { clip = true } - .onCameraControl(scope = scope, scene = scene) - .onCursorControl(camera = scene.camera, cursor = cursors.first()) - .drawLayers(camera = scene.camera, layers = scene.layers) - .drawElements(camera = scene.camera, elements = scene.elements) - .drawCursors(camera = scene.camera, cursors = cursors) - .drawFogOfWar(scene = scene), + .onCameraControl(scope = scope, sceneSize = scene.size, camera = camera) + .drawLayers(camera = camera, layers = scene.layers) + .drawElements(camera = camera, elements = scene.elements) + .then(other = modifier), ) { content() - - SceneDebugPanel( - modifier = Modifier - .align(alignment = Alignment.TopEnd) - .padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues), - cursor = cursors.first(), - scene = scene, - ) } } @@ -138,83 +99,6 @@ fun rememberElementFromResource( } } -@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) -fun Modifier.onCameraControl( - scope: CoroutineScope, - scene: Scene, -): Modifier { - val offsetDelta = CursorDelta() - return this - .onSizeChanged { - scene.camera.changeSizes( - sceneSize = scene.size, - cameraSize = it, - ) - } - .onPointerEvent(PointerEventType.Move) { event: PointerEvent -> - scope.launch { - offsetDelta.handlePositionChange( - event = event, - ) { delta -> - when { - event.buttons.isTertiaryPressed || (event.keyboardModifiers.isCtrlPressed && event.buttons.isPrimaryPressed) -> scene.camera.handlePanning( - delta = delta, - snap = true, - ) - - event.keyboardModifiers.isAltPressed -> scene.camera.handleZoom( - zoomIn = delta.y.sign < 0f, - power = 0.025f, - snap = true, - ) - } - } - } - } - .onPointerEvent(PointerEventType.Scroll) { event: PointerEvent -> - scope.launch { - scene.camera.handleZoom( - zoomIn = event.changes.first().scrollDelta.y.sign < 0f, - power = 0.15f, - snap = false, - ) - } - } -} - -@OptIn(ExperimentalComposeUiApi::class) -fun Modifier.onCursorControl( - camera: Camera, - cursor: Cursor, -): Modifier = this - .onPointerEvent(PointerEventType.Exit) { event: PointerEvent -> - cursor.change( - position = Offset.Unspecified, - ) - } - .onPointerEvent(PointerEventType.Move) { event: PointerEvent -> - cursor.change( - position = event.changes.first().position.global(camera = camera), - ) - } - -private fun Modifier.drawCursors( - camera: Camera, - cursors: List, -): Modifier = this - .drawWithCache { - onDrawBehind { - cursors.forEach { cursor -> - drawRect( - color = Color.Green, - topLeft = cursor.offset.local(camera = camera), - size = Size(10f, 10f), - style = Stroke(width = 2f), - ) - } - } - } - @OptIn(ExperimentalComposeUiApi::class) fun Modifier.drawLayers( camera: Camera, @@ -254,61 +138,4 @@ fun Modifier.drawElements( ) } } -} - -fun Modifier.drawFogOfWar( - scene: Scene, -): Modifier = this - .graphicsLayer( - compositingStrategy = CompositingStrategy.Offscreen - ) - .drawWithCache { - val stroke = Stroke( - width = 32f, - cap = StrokeCap.Round, - join = StrokeJoin.Round - ) - val fog = Color.Black.copy(alpha = 0.5f) - val color = Color.Transparent - onDrawBehind { - drawRect( - color = fog, - ) - scale( - scale = 1 / scene.camera.zoom, - pivot = Offset.Zero, - ) { - translate( - left = -scene.camera.offset.x.toFloat(), - top = -scene.camera.offset.y.toFloat(), - ) { - drawPath( - path = scene.fogOfWar.path, - style = stroke, - color = color, - blendMode = BlendMode.Clear, - ) - } - } - } - } - -private data class CursorDelta( - var lastDeltaTimestamp: Long = System.currentTimeMillis(), - var previousPosition: Offset = Offset.Zero, - var currentPosition: Offset = Offset.Zero, -) { - suspend inline fun handlePositionChange( - event: PointerEvent, - delay: Float = 10f, - crossinline block: suspend (delta: Offset) -> Unit, - ) { - val currentTimestamp = System.currentTimeMillis() - if (currentTimestamp - lastDeltaTimestamp > delay) { - lastDeltaTimestamp = currentTimestamp - previousPosition = currentPosition - currentPosition = event.changes.first().position - block(currentPosition - previousPosition) - } - } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Camera.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Camera.kt new file mode 100644 index 0000000..cd48fab --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Camera.kt @@ -0,0 +1,97 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.camera + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.VectorConverter +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.util.fastRoundToInt + +@Stable +class Camera( + private val initialZoom: Float = 2f, + private val initialOffset: IntOffset = IntOffset.Zero, +) { + private var _zoom = Animatable( + initialValue = initialZoom, + typeConverter = Float.VectorConverter, + ) + val zoom: Float get() = _zoom.value + + private var _offset = Animatable( + initialValue = initialOffset, + typeConverter = IntOffset.VectorConverter, + ) + val offset: IntOffset by derivedStateOf { + _offset.value + IntOffset( + x = (_sceneSize.width - cameraSizeZoomed.width) / 2, + y = (_sceneSize.height - cameraSizeZoomed.height) / 2, + ) + } + + private var _sceneSize: IntSize by mutableStateOf(IntSize.Zero) + private var _cameraSize: IntSize by mutableStateOf(IntSize.Zero) + val cameraSize: IntSize get() = _cameraSize + val cameraSizeZoomed: IntSize by derivedStateOf { + IntSize( + width = (cameraSize.width * zoom).fastRoundToInt(), + height = (cameraSize.height * zoom).fastRoundToInt(), + ) + } + + fun changeSizes( + sceneSize: IntSize, + cameraSize: IntSize, + ) { + _cameraSize = cameraSize + _sceneSize = sceneSize + } + + suspend fun handlePanning( + delta: Offset, + snap: Boolean, + ) { + val value = _offset.value - IntOffset( + x = (delta.x * zoom).fastRoundToInt(), + y = (delta.y * zoom).fastRoundToInt(), + ) + when { + snap -> _offset.snapTo(targetValue = value) + else -> _offset.animateTo(targetValue = value) + } + } + + suspend fun handleZoom( + power: Float, + snap: Boolean = false, + ) { + val value = _zoom.value * (1f - power) + when { + snap -> _zoom.snapTo(targetValue = value) + else -> _zoom.animateTo(targetValue = value) + } + } + + suspend fun resetPosition( + snap: Boolean = false, + ) { + when (snap) { + true -> _offset.snapTo(targetValue = initialOffset) + else -> _offset.animateTo(targetValue = initialOffset) + } + } + + suspend fun resetZoom( + snap: Boolean = false, + ) { + when (snap) { + true -> _zoom.snapTo(targetValue = initialZoom) + else -> _zoom.animateTo(targetValue = initialZoom) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Modifier+onCameraControl.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Modifier+onCameraControl.kt new file mode 100644 index 0000000..ddd3218 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/camera/Modifier+onCameraControl.kt @@ -0,0 +1,75 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.camera + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.PointerEvent +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.isCtrlPressed +import androidx.compose.ui.input.pointer.isPrimaryPressed +import androidx.compose.ui.input.pointer.isTertiaryPressed +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.unit.IntSize +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlin.math.sign + +@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) +fun Modifier.onCameraControl( + scope: CoroutineScope, + sceneSize: IntSize, + camera: Camera, +): Modifier { + val offsetDelta = CursorDelta() + return this + .onSizeChanged { + camera.changeSizes( + sceneSize = sceneSize, + cameraSize = it, + ) + } + .onPointerEvent(PointerEventType.Move) { event: PointerEvent -> + scope.launch { + offsetDelta.handlePositionChange( + event = event, + ) { delta -> + when { + event.buttons.isTertiaryPressed || (event.keyboardModifiers.isCtrlPressed && event.buttons.isPrimaryPressed) -> camera.handlePanning( + delta = delta, + snap = true, + ) + } + } + } + } + .onPointerEvent(PointerEventType.Scroll) { event: PointerEvent -> + scope.launch { + camera.handleZoom( + power = -event.changes.first().scrollDelta.y.sign * 0.15f, + snap = false, + ) + } + } +} + +private data class CursorDelta( + var lastDeltaTimestamp: Long = System.currentTimeMillis(), + var previousPosition: Offset = Offset.Zero, + var currentPosition: Offset = Offset.Zero, +) { + suspend inline fun handlePositionChange( + event: PointerEvent, + delay: Float = 10f, + crossinline block: suspend (delta: Offset) -> Unit, + ) { + val currentTimestamp = System.currentTimeMillis() + if (currentTimestamp - lastDeltaTimestamp > delay) { + lastDeltaTimestamp = currentTimestamp + previousPosition = currentPosition + currentPosition = event.changes.first().position + block(currentPosition - previousPosition) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Cursor.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Cursor.kt new file mode 100644 index 0000000..f9a9b78 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Cursor.kt @@ -0,0 +1,24 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.cursor + +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.unit.IntOffset + +@Stable +class Cursor( + initial: IntOffset = IntOffset.Zero, +) { + private val _offset = mutableStateOf(initial) + val offset by _offset + + fun change( + position: Offset, + ) { + _offset.value = IntOffset( + x = position.x.toInt(), + y = position.y.toInt(), + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+drawCursor.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+drawCursor.kt new file mode 100644 index 0000000..d11ca1c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+drawCursor.kt @@ -0,0 +1,26 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.cursor + +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera +import com.pixelized.desktop.lwa.ui.composable.scene.utils.local + +fun Modifier.drawCursors( + camera: Camera, + cursors: List, +): Modifier = this + .drawWithCache { + onDrawBehind { + cursors.forEach { cursor -> + drawRect( + color = Color.Green, + topLeft = cursor.offset.local(camera = camera), + size = Size(10f, 10f), + style = Stroke(width = 2f), + ) + } + } + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+onCursorControl.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+onCursorControl.kt new file mode 100644 index 0000000..c198f21 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/cursor/Modifier+onCursorControl.kt @@ -0,0 +1,26 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.cursor + +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.PointerEvent +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera +import com.pixelized.desktop.lwa.ui.composable.scene.utils.global + +@OptIn(ExperimentalComposeUiApi::class) +fun Modifier.onCursorControl( + camera: Camera, + cursor: Cursor, +): Modifier = this + .onPointerEvent(PointerEventType.Exit) { event: PointerEvent -> + cursor.change( + position = Offset.Unspecified, + ) + } + .onPointerEvent(PointerEventType.Move) { event: PointerEvent -> + cursor.change( + position = event.changes.first().position.global(camera = camera), + ) + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CameraDebugPanel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CameraDebugPanel.kt new file mode 100644 index 0000000..596f1b4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CameraDebugPanel.kt @@ -0,0 +1,82 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.debug + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera +import com.pixelized.desktop.lwa.ui.theme.lwa +import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography + +@Composable +fun CameraDebugPanel( + modifier: Modifier = Modifier, + style: LwaTypography.Debug = MaterialTheme.lwa.typography.debug, + padding: Dp = MaterialTheme.lwa.dimen.debug.offset, + spacing: Dp = 2.dp, + camera: Camera, + isOpen: Boolean = true, +) { + val isOpen = remember { mutableStateOf(isOpen) } + + Column( + modifier = Modifier + .clickable { isOpen.value = isOpen.value.not() } + .widthIn(min = MaterialTheme.lwa.dimen.debug.panelWidth) + .animateContentSize() + .then(other = modifier), + verticalArrangement = Arrangement.spacedBy(space = spacing), + ) { + Text( + style = MaterialTheme.lwa.typography.debug.title, + color = MaterialTheme.lwa.colorScheme.base.primary, + text = "Camera", + ) + if (isOpen.value) { + Text( + modifier = Modifier.padding(start = padding), + style = MaterialTheme.lwa.typography.debug.propertyValue, + text = buildAnnotatedString { + withStyle(style.propertyIdSpan) { append("offset: ") } + append(camera.offset.toString()) + }, + ) + Text( + modifier = Modifier.padding(start = padding), + style = MaterialTheme.lwa.typography.debug.propertyValue, + text = buildAnnotatedString { + withStyle(style.propertyIdSpan) { append("size: ") } + append(camera.cameraSize.toString()) + }, + ) + Text( + modifier = Modifier.padding(start = padding), + style = MaterialTheme.lwa.typography.debug.propertyValue, + text = buildAnnotatedString { + withStyle(style.propertyIdSpan) { append("zoom: ") } + append(camera.zoom.toString()) + }, + ) + Text( + modifier = Modifier.padding(start = padding), + style = MaterialTheme.lwa.typography.debug.propertyValue, + text = buildAnnotatedString { + withStyle(style.propertyIdSpan) { append("projection: ") } + append(camera.cameraSizeZoomed.toString()) + }, + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Cursor.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CursorDebugPanel.kt similarity index 59% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Cursor.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CursorDebugPanel.kt index a3ee09b..bdc5fb0 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/Cursor.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/CursorDebugPanel.kt @@ -1,5 +1,6 @@ -package com.pixelized.desktop.lwa.ui.composable.scene +package com.pixelized.desktop.lwa.ui.composable.scene.debug +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -8,44 +9,25 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.composable.scene.cursor.Cursor import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography -@Stable -class Cursor( - initial: IntOffset = IntOffset.Zero, -) { - private val _offset = mutableStateOf(initial) - val offset by _offset - - fun change( - position: Offset, - ) { - _offset.value = IntOffset( - x = position.x.toInt(), - y = position.y.toInt(), - ) - } -} - @Composable -fun SceneCursorDebug( +fun CursorDebugPanel( modifier: Modifier = Modifier, - cursor: Cursor, - isOpen: Boolean = true, style: LwaTypography.Debug = MaterialTheme.lwa.typography.debug, padding: Dp = MaterialTheme.lwa.dimen.debug.offset, + spacing: Dp = 2.dp, + cursors: List, + isOpen: Boolean = true, ) { val isOpen = remember { mutableStateOf(isOpen) } @@ -53,8 +35,9 @@ fun SceneCursorDebug( modifier = Modifier .clickable { isOpen.value = isOpen.value.not() } .widthIn(min = MaterialTheme.lwa.dimen.debug.panelWidth) + .animateContentSize() .then(other = modifier), - verticalArrangement = Arrangement.spacedBy(space = 2.dp), + verticalArrangement = Arrangement.spacedBy(space = spacing), ) { Text( style = MaterialTheme.lwa.typography.debug.title, @@ -62,14 +45,16 @@ fun SceneCursorDebug( text = "Cursor", ) if (isOpen.value) { - Text( - modifier = Modifier.padding(start = padding), - style = MaterialTheme.lwa.typography.debug.propertyValue, - text = buildAnnotatedString { - withStyle(style.propertyId) { append("offset: ") } - append(cursor.offset.toString()) - }, - ) + cursors.forEach { cursor -> + Text( + modifier = Modifier.padding(start = padding), + style = MaterialTheme.lwa.typography.debug.propertyValue, + text = buildAnnotatedString { + withStyle(style.propertyIdSpan) { append("coordinate: ") } + append(cursor.offset.toString()) + }, + ) + } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/SceneDebugPanel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/SceneDebugPanel.kt similarity index 58% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/SceneDebugPanel.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/SceneDebugPanel.kt index 45c99b7..d2a0ec0 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/SceneDebugPanel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/debug/SceneDebugPanel.kt @@ -1,15 +1,11 @@ -package com.pixelized.desktop.lwa.ui.composable.scene +package com.pixelized.desktop.lwa.ui.composable.scene.debug import androidx.compose.animation.animateContentSize import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Card import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -20,6 +16,7 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.composable.scene.Scene import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneElementDebug import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneLayerDebug import com.pixelized.desktop.lwa.ui.theme.lwa @@ -28,74 +25,11 @@ import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography @Composable fun SceneDebugPanel( modifier: Modifier = Modifier, - paddingValues: PaddingValues = MaterialTheme.lwa.dimen.paddingValues, - cursor: Cursor, - scene: Scene, -) { - Card( - modifier = Modifier - .verticalScroll(state = rememberScrollState()) - .then(other = modifier), - backgroundColor = MaterialTheme.lwa.colorScheme.elevated.base4dp, - ) { - Column( - modifier = Modifier.padding(paddingValues = paddingValues), - verticalArrangement = Arrangement.spacedBy(space = 8.dp), - ) { - SceneDebug( - modifier = Modifier.animateContentSize(), - scene = scene, - ) - SceneCursorDebug( - modifier = Modifier.animateContentSize(), - cursor = cursor, - ) - SceneCameraDebug( - modifier = Modifier.animateContentSize(), - camera = scene.camera, - ) - Column { - Text( - style = MaterialTheme.lwa.typography.debug.title, - text = "Layers:(${scene.layers.size})" - ) - scene.layers.forEach { layer -> - SceneLayerDebug( - modifier = Modifier - .animateContentSize() - .padding(start = MaterialTheme.lwa.dimen.debug.offset), - isOpen = false, - layer = layer, - ) - } - } - Column { - Text( - style = MaterialTheme.lwa.typography.debug.title, - text = "Elements:(${scene.elements.size})" - ) - scene.elements.forEach { element -> - SceneElementDebug( - modifier = Modifier - .animateContentSize() - .padding(start = MaterialTheme.lwa.dimen.debug.offset), - isOpen = false, - element = element, - ) - } - } - } - } - -} - -@Composable -private fun SceneDebug( - modifier: Modifier = Modifier, - scene: Scene, - isOpen: Boolean = true, style: LwaTypography.Debug = MaterialTheme.lwa.typography.debug, padding: Dp = MaterialTheme.lwa.dimen.debug.offset, + spacing: Dp = 2.dp, + scene: Scene, + isOpen: Boolean = true, ) { val isOpen = remember { mutableStateOf(isOpen) } @@ -103,8 +37,9 @@ private fun SceneDebug( modifier = Modifier .clickable { isOpen.value = isOpen.value.not() } .widthIn(min = MaterialTheme.lwa.dimen.debug.panelWidth) + .animateContentSize() .then(other = modifier), - verticalArrangement = Arrangement.spacedBy(space = 2.dp), + verticalArrangement = Arrangement.spacedBy(space = spacing), ) { Text( style = MaterialTheme.lwa.typography.debug.title, @@ -116,10 +51,40 @@ private fun SceneDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("Size: ") } + withStyle(style.propertyIdSpan) { append("Size: ") } append(scene.size.toString()) }, ) + Column( + modifier = Modifier.padding(start = padding), + ) { + Text( + style = MaterialTheme.lwa.typography.debug.propertyId, + text = "Layers:(${scene.layers.size})" + ) + scene.layers.forEach { layer -> + SceneLayerDebug( + modifier = Modifier.padding(start = padding), + isOpen = false, + layer = layer, + ) + } + } + Column( + modifier = Modifier.padding(start = padding), + ) { + Text( + style = MaterialTheme.lwa.typography.debug.propertyId, + text = "Elements:(${scene.elements.size})" + ) + scene.elements.forEach { element -> + SceneElementDebug( + modifier = Modifier.padding(start = padding), + isOpen = false, + element = element, + ) + } + } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneElement.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneElement.kt index fae7da7..a900c86 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneElement.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneElement.kt @@ -71,7 +71,7 @@ fun SceneElementDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("id: ") } + withStyle(style.propertyIdSpan) { append("id: ") } append(element.id) }, ) @@ -79,7 +79,7 @@ fun SceneElementDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("offset: ") } + withStyle(style.propertyIdSpan) { append("offset: ") } append(element.offset.toString()) }, ) @@ -87,7 +87,7 @@ fun SceneElementDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("size: ") } + withStyle(style.propertyIdSpan) { append("size: ") } append(element.size.toString()) }, ) @@ -95,7 +95,7 @@ fun SceneElementDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("alpha: ") } + withStyle(style.propertyIdSpan) { append("alpha: ") } append(element.alpha.toString()) }, ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneLayer.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneLayer.kt index 6705afe..4ac34e1 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneLayer.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/drawables/SceneLayer.kt @@ -57,7 +57,7 @@ fun SceneLayerDebug( verticalArrangement = Arrangement.spacedBy(space = 2.dp), ) { Text( - style = MaterialTheme.lwa.typography.debug.title, + style = MaterialTheme.lwa.typography.debug.propertyId, color = MaterialTheme.lwa.colorScheme.base.primary, text = layer.name, ) @@ -66,7 +66,7 @@ fun SceneLayerDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("id: ") } + withStyle(style.propertyIdSpan) { append("id: ") } append(layer.id) }, ) @@ -74,7 +74,7 @@ fun SceneLayerDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("offset: ") } + withStyle(style.propertyIdSpan) { append("offset: ") } append(layer.offset.toString()) }, ) @@ -82,7 +82,7 @@ fun SceneLayerDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("size: ") } + withStyle(style.propertyIdSpan) { append("size: ") } append(layer.size.toString()) }, ) @@ -90,7 +90,7 @@ fun SceneLayerDebug( modifier = Modifier.padding(start = padding), style = MaterialTheme.lwa.typography.debug.propertyValue, text = buildAnnotatedString { - withStyle(style.propertyId) { append("alpha: ") } + withStyle(style.propertyIdSpan) { append("alpha: ") } append(layer.alpha.toString()) }, ) diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/FogOfWar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/FogOfWar.kt new file mode 100644 index 0000000..e68ebdb --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/FogOfWar.kt @@ -0,0 +1,44 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.fogOfWar + +import androidx.compose.runtime.Stable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.drawscope.scale +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.PointerEvent +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.isPrimaryPressed +import androidx.compose.ui.input.pointer.onPointerEvent +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera +import com.pixelized.desktop.lwa.ui.composable.scene.utils.global +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +@Stable +class FogOfWar( + val color: Color = Color.Black.copy(alpha = 0.5f), +) { + val path = Path() + + fun moveTo(position: Offset) { + path.moveTo(x = position.x, y = position.y) + } + + fun lineTo(position: Offset) { + path.lineTo(x = position.x, y = position.y) + } + + companion object { + val NONE = FogOfWar(color = Color.Transparent) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+drawFogOfWar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+drawFogOfWar.kt new file mode 100644 index 0000000..0e8e7ec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+drawFogOfWar.kt @@ -0,0 +1,53 @@ +package com.pixelized.desktop.lwa.ui.composable.scene.fogOfWar + +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.drawscope.scale +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.graphics.graphicsLayer +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera + +fun Modifier.drawFogOfWar( + camera: Camera, + fogOfWar: FogOfWar, +): Modifier = this + .graphicsLayer( + compositingStrategy = CompositingStrategy.Offscreen + ) + .drawWithCache { + val stroke = Stroke( + width = 32f, + cap = StrokeCap.Round, + join = StrokeJoin.Round + ) + val fog = Color.Black.copy(alpha = 0.5f) + val color = Color.Transparent + onDrawBehind { + drawRect( + color = fog, + ) + scale( + scale = 1 / camera.zoom, + pivot = Offset.Zero, + ) { + translate( + left = -camera.offset.x.toFloat(), + top = -camera.offset.y.toFloat(), + ) { + drawPath( + path = fogOfWar.path, + style = stroke, + color = color, + blendMode = BlendMode.Clear, + ) + } + } + } + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/FogOfWar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+onFogOfWarControl.kt similarity index 74% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/FogOfWar.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+onFogOfWarControl.kt index b8bec27..3d8f576 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/FogOfWar.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/fogOfWar/Modifier+onFogOfWarControl.kt @@ -1,38 +1,16 @@ -package com.pixelized.desktop.lwa.ui.composable.scene +package com.pixelized.desktop.lwa.ui.composable.scene.fogOfWar -import androidx.compose.runtime.Stable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Path import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.isPrimaryPressed import androidx.compose.ui.input.pointer.onPointerEvent +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera import com.pixelized.desktop.lwa.ui.composable.scene.utils.global import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@Stable -class FogOfWar( - val color: Color = Color.Black.copy(alpha = 0.5f), -) { - val path = Path() - - fun moveTo(position: Offset) { - path.moveTo(x = position.x, y = position.y) - } - - fun lineTo(position: Offset) { - path.lineTo(x = position.x, y = position.y) - } - - companion object { - val NONE = FogOfWar(color = Color.Transparent) - } -} - @OptIn(ExperimentalComposeUiApi::class) fun Modifier.onFogOfWarControl( scope: CoroutineScope, @@ -46,7 +24,7 @@ fun Modifier.onFogOfWarControl( var previousEvent: PointerEvent? = null this - .onPointerEvent(PointerEventType.Release) { event: PointerEvent -> + .onPointerEvent(PointerEventType.Release) { _: PointerEvent -> scope.launch { println("PointerEventType.Release") lastEventTime = System.currentTimeMillis() diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/Offset+Camera.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/Offset+Coordinate.kt similarity index 94% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/Offset+Camera.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/Offset+Coordinate.kt index 853a544..603a99f 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/Offset+Camera.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/utils/Offset+Coordinate.kt @@ -2,7 +2,7 @@ package com.pixelized.desktop.lwa.ui.composable.scene.utils import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntOffset -import com.pixelized.desktop.lwa.ui.composable.scene.Camera +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera /** * Convert local positon to global one. diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/WindowNavHost.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/WindowNavHost.kt index 206ac66..e86c7f4 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/WindowNavHost.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/WindowNavHost.kt @@ -34,7 +34,7 @@ class WindowController( private val _windows = mutableStateOf>(emptyMap()) val windows: State> get() = _windows - fun showWindow(window: Window) { + fun openWindow(window: Window) { _windows.value = _windows.value.toMutableMap().apply { this[window.id] = window } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt index 39b1837..13322e9 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/GameMasterWindow.kt @@ -14,10 +14,10 @@ class GameMasterWindow( size = size, ) -fun WindowController.navigateToGameMasterWindow( +fun WindowController.openGameMasterWindow( title: String = "Game master", ) { - showWindow( + openWindow( window = GameMasterWindow( title = title, size = DpSize( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt index 9abb099..ad42ae4 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt @@ -44,7 +44,7 @@ import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterShe import com.pixelized.desktop.lwa.ui.composable.character.diminished.CharacterSheetDiminishedDialogViewModel import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler -import com.pixelized.desktop.lwa.ui.composable.scene.MapScene +import com.pixelized.desktop.lwa.ui.screen.campaign.map.MapScene import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToLevelScreen import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlay @@ -55,8 +55,8 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.rememberTransi import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbon import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.NpcRibbonViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbonViewModel -import com.pixelized.desktop.lwa.ui.screen.campaign.text.CampaignChat -import com.pixelized.desktop.lwa.ui.screen.campaign.text.CampaignChatViewModel +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.CampaignChat +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.CampaignChatViewModel import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbar import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbarViewModel import com.pixelized.desktop.lwa.ui.theme.lwa @@ -101,9 +101,7 @@ fun CampaignScreen( }, main = { - MapScene( - modifier = Modifier.matchParentSize(), - ) + }, chat = { CampaignChat( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/CampaignChat.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/CampaignChat.kt similarity index 89% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/CampaignChat.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/CampaignChat.kt index f83298d..f4c505a 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/CampaignChat.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/CampaignChat.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState @@ -42,15 +42,15 @@ 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.text.messages.CharacteristicTextMessage -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.CharacteristicTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.DiminishedTextMessage -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.DiminishedTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.PurseTextMessage -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.PurseTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.RollTextMessage -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.RollTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.TextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.CharacteristicTextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.CharacteristicTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.DiminishedTextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.DiminishedTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.PurseTextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.PurseTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.RollTextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.RollTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.TextMessage import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.usecase.SettingsUseCase import lwacharactersheet.composeapp.generated.resources.Res diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/CampaignChatViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/CampaignChatViewModel.kt similarity index 97% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/CampaignChatViewModel.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/CampaignChatViewModel.kt index a2fab42..92386f9 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/CampaignChatViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/CampaignChatViewModel.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/TextMessageFactory.kt similarity index 94% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/TextMessageFactory.kt index 373c3a4..53192a9 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/TextMessageFactory.kt @@ -1,12 +1,12 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.settings.model.Settings -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.CharacteristicTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.DiminishedTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.PurseTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.RollTextMessageUio -import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.TextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.CharacteristicTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.DiminishedTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.PurseTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.RollTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.TextMessage import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/CharacteristicTextMessage.kt similarity index 96% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/CharacteristicTextMessage.kt index 40568f8..f3add65 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/CharacteristicTextMessage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/CharacteristicTextMessage.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text.messages +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/DiminishedTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/DiminishedTextMessage.kt similarity index 96% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/DiminishedTextMessage.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/DiminishedTextMessage.kt index a7491e1..0f9f0f1 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/DiminishedTextMessage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/DiminishedTextMessage.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text.messages +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/PurseTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/PurseTextMessage.kt similarity index 97% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/PurseTextMessage.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/PurseTextMessage.kt index 0030718..4a5b868 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/PurseTextMessage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/PurseTextMessage.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text.messages +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.TooltipPlacement @@ -8,7 +8,6 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/RollTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/RollTextMessage.kt similarity index 98% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/RollTextMessage.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/RollTextMessage.kt index 8287452..a76d392 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/RollTextMessage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/RollTextMessage.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text.messages +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/TextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/TextMessage.kt similarity index 53% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/TextMessage.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/TextMessage.kt index 4965e03..1162867 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/messages/TextMessage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chatbox/messages/TextMessage.kt @@ -1,4 +1,4 @@ -package com.pixelized.desktop.lwa.ui.screen.campaign.text.messages +package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages sealed interface TextMessage { val id : String diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/map/DahomeMap.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/map/DahomeMap.kt new file mode 100644 index 0000000..205cb2b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/map/DahomeMap.kt @@ -0,0 +1,205 @@ +package com.pixelized.desktop.lwa.ui.screen.campaign.map + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.pixelized.desktop.lwa.ui.composable.scene.Scene +import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera +import com.pixelized.desktop.lwa.ui.composable.scene.cursor.Cursor +import com.pixelized.desktop.lwa.ui.composable.scene.cursor.onCursorControl +import com.pixelized.desktop.lwa.ui.composable.scene.debug.CameraDebugPanel +import com.pixelized.desktop.lwa.ui.composable.scene.debug.CursorDebugPanel +import com.pixelized.desktop.lwa.ui.composable.scene.debug.SceneDebugPanel +import com.pixelized.desktop.lwa.ui.composable.scene.rememberLayoutFromResource +import com.pixelized.desktop.lwa.ui.theme.lwa +import kotlinx.coroutines.launch +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.ic_center_focus_weak_24dp +import lwacharactersheet.composeapp.generated.resources.ic_frame_bug_24dp +import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp +import lwacharactersheet.composeapp.generated.resources.ic_zoom_in_map_24dp +import lwacharactersheet.composeapp.generated.resources.ic_zoom_out_map_24dp +import lwacharactersheet.composeapp.generated.resources.image_dahome_maps +import lwacharactersheet.composeapp.generated.resources.image_dahome_regions +import org.jetbrains.compose.resources.painterResource + +@Composable +fun MapScene( + modifier: Modifier = Modifier, + enableDebug: Boolean, +) { + val scope = rememberCoroutineScope() + + val map = rememberLayoutFromResource( + name = "Dahomé", + resource = Res.drawable.image_dahome_maps, + ) + val mapRegionOverlay = rememberLayoutFromResource( + name = "Région", + resource = Res.drawable.image_dahome_regions, + ) + val camera = remember { + Camera( + initialZoom = 1f, + initialOffset = IntOffset(x = -150, y = -120), + ) + } + val cursor = remember { + Cursor() + } + val scene = remember(map, mapRegionOverlay) { + Scene( + layers = listOf(map, mapRegionOverlay), + elements = emptyList(), + ) + } + val openDebugMenu = remember { + mutableStateOf(false) + } + Scene( + modifier = Modifier + .onCursorControl(camera = camera, cursor = cursor) + .then(other = modifier), + scene = scene, + camera = camera, + ) { + MapActions( + modifier = Modifier + .align(alignment = Alignment.BottomEnd) + .padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues), + enableDebug = enableDebug, + onZoomIn = { + scope.launch { + camera.handleZoom(power = 0.3f) + } + }, + onZoomOut = { + scope.launch { + camera.handleZoom(power = -0.3f) + } + }, + onResetCamera = { + scope.launch { + camera.resetPosition() + } + scope.launch { + camera.resetZoom() + } + }, + onToggleLayer = { + scope.launch { + scene.layers.getOrNull(1)?.let { + it.alpha(alpha = if (it.alpha == 0f) 1f else 0f) + } + } + }, + onToggleDebug = { + openDebugMenu.value = openDebugMenu.value.not() + }, + ) + + AnimatedVisibility( + modifier = Modifier.align(alignment = Alignment.TopEnd), + visible = openDebugMenu.value, + ) { + Card( + modifier = Modifier + .padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues) + .verticalScroll(state = rememberScrollState()), + backgroundColor = MaterialTheme.lwa.colorScheme.elevated.base4dp, + ) { + Column( + modifier = Modifier.padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues), + verticalArrangement = Arrangement.spacedBy(space = 4.dp), + ) { + SceneDebugPanel( + scene = scene, + ) + CameraDebugPanel( + camera = camera, + ) + CursorDebugPanel( + cursors = remember { listOf(cursor) }, + ) + } + } + } + } +} + +@Composable +private fun MapActions( + modifier: Modifier = Modifier, + enableDebug: Boolean, + onZoomIn: () -> Unit, + onZoomOut: () -> Unit, + onResetCamera: () -> Unit, + onToggleLayer: () -> Unit, + onToggleDebug: () -> Unit, +) { + Row( + modifier = modifier, + ) { + IconButton( + onClick = onZoomIn, + ) { + Icon( + painter = painterResource(Res.drawable.ic_zoom_in_map_24dp), + contentDescription = null + ) + } + IconButton( + onClick = onZoomOut, + ) { + Icon( + painter = painterResource(Res.drawable.ic_zoom_out_map_24dp), + contentDescription = null + ) + } + IconButton( + onClick = onResetCamera, + ) { + Icon( + painter = painterResource(Res.drawable.ic_center_focus_weak_24dp), + contentDescription = null + ) + } + IconButton( + onClick = onToggleLayer, + ) { + Icon( + painter = painterResource(Res.drawable.ic_visibility_24dp), + contentDescription = null + ) + } + AnimatedVisibility( + visible = enableDebug, + ) { + IconButton( + onClick = onToggleDebug, + ) { + Icon( + painter = painterResource(Res.drawable.ic_frame_bug_24dp), + contentDescription = null + ) + } + } + } +} \ No newline at end of file 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 b95586e..fc8ceca 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,25 +13,25 @@ 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.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle 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.destination.navigateToSettings -import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToGameMasterWindow +import com.pixelized.desktop.lwa.ui.navigation.window.destination.openGameMasterWindow +import com.pixelized.desktop.lwa.ui.screen.campaign.map.MapScene import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.links.ResourcesMenu import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.network.NetworkMenu import com.pixelized.desktop.lwa.ui.theme.lwa import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.ic_link_24dp +import lwacharactersheet.composeapp.generated.resources.ic_map_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 @@ -41,9 +41,10 @@ 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), +data class CampaignMenuStateUio( + val isResourcesMenuOpen: Boolean, + val isNetworkMenuOpen: Boolean, + val isMapMenuOpen: Boolean, ) @Composable @@ -53,51 +54,47 @@ fun CampaignToolbar( val windows = LocalWindowController.current val screen = LocalScreenController.current - val menuState = remember { MenuState() } - - val title = viewModel.title.collectAsState() - val status = viewModel.status.collectAsState() - val isAdmin = viewModel.isAdmin.collectAsState() + val title = viewModel.title.collectAsStateWithLifecycle() + val status = viewModel.status.collectAsStateWithLifecycle() + val isAdmin = viewModel.isAdmin.collectAsStateWithLifecycle() + val menusState = viewModel.menusState.collectAsStateWithLifecycle() CampaignToolbarContent( title = title, status = status, isAdmin = isAdmin, - state = menuState, + menusState = menusState, onAdmin = { - windows.navigateToGameMasterWindow() - }, - onNetwork = { - menuState.isNetworkMenuOpen.value = true - }, - onResources = { - menuState.isResourcesMenuOpen.value = true + windows.openGameMasterWindow() }, onSettings = { screen.navigateToSettings() }, - onDismissNetworkMenu = { - menuState.isNetworkMenuOpen.value = false - }, - onDismissResourcesMenu = { - menuState.isResourcesMenuOpen.value = false - }, + onNetwork = viewModel::onNetwork, + onResources = viewModel::onResources, + onMap = viewModel::onMap, + onDismissNetworkMenu = viewModel::onDismissNetworkMenu, + onDismissResourcesMenu = viewModel::onDismissResourcesMenu, + onDismissMapMenu = viewModel::onDismissMapMenu, ) } +@OptIn(ExperimentalComposeUiApi::class) @Composable private fun CampaignToolbarContent( modifier: Modifier = Modifier, title: State, status: State, isAdmin: State, - state: MenuState, + menusState: State, onAdmin: () -> Unit, onNetwork: () -> Unit, + onMap: () -> Unit, onResources: () -> Unit, onSettings: () -> Unit, onDismissNetworkMenu: () -> Unit, onDismissResourcesMenu: () -> Unit, + onDismissMapMenu: () -> Unit, ) { TopAppBar( modifier = modifier, @@ -121,6 +118,25 @@ private fun CampaignToolbarContent( ) } } + IconButton( + onClick = onMap, + ) { + Icon( + painter = painterResource(Res.drawable.ic_map_24dp), + tint = MaterialTheme.colors.primary, + contentDescription = null, + ) + DropdownMenu( + expanded = menusState.value.isMapMenuOpen, + onDismissRequest = onDismissMapMenu, + content = { + MapScene( + modifier = Modifier.size(640.dp), + enableDebug = isAdmin.value, + ) + }, + ) + } IconButton( onClick = onResources, ) { @@ -130,7 +146,7 @@ private fun CampaignToolbarContent( contentDescription = null, ) DropdownMenu( - expanded = state.isResourcesMenuOpen.value, + expanded = menusState.value.isResourcesMenuOpen, onDismissRequest = onDismissResourcesMenu, content = { ResourcesMenu( @@ -156,7 +172,7 @@ private fun CampaignToolbarContent( contentDescription = null, ) DropdownMenu( - expanded = state.isNetworkMenuOpen.value, + expanded = menusState.value.isNetworkMenuOpen, onDismissRequest = onDismissNetworkMenu, content = { NetworkMenu( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbarViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbarViewModel.kt index 96d0798..fbc3b7b 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbarViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbarViewModel.kt @@ -3,20 +3,30 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository -import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository import com.pixelized.desktop.lwa.repository.network.NetworkRepository import com.pixelized.desktop.lwa.repository.settings.SettingsRepository +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update class CampaignToolbarViewModel( - private val characterRepository: CharacterSheetRepository, - private val campaignRepository: CampaignRepository, + campaignRepository: CampaignRepository, networkRepository: NetworkRepository, settingsRepository: SettingsRepository, ) : ViewModel() { + private val _menusState = MutableStateFlow( + CampaignMenuStateUio( + isResourcesMenuOpen = false, + isNetworkMenuOpen = false, + isMapMenuOpen = false + ) + ) + val menusState: StateFlow = _menusState + val status = networkRepository.status val title = campaignRepository.campaignFlow() @@ -34,4 +44,36 @@ class CampaignToolbarViewModel( started = SharingStarted.Lazily, initialValue = false, ) + + val isGameMaster = settingsRepository.settingsFlow() + .map { it.isGameMaster ?: false } + .stateIn( + scope = viewModelScope, + started = SharingStarted.Lazily, + initialValue = false, + ) + + fun onNetwork() { + _menusState.update { it.copy(isNetworkMenuOpen = true) } + } + + fun onResources() { + _menusState.update { it.copy(isResourcesMenuOpen = true) } + } + + fun onMap() { + _menusState.update { it.copy(isMapMenuOpen = true) } + } + + fun onDismissNetworkMenu() { + _menusState.update { it.copy(isNetworkMenuOpen = false) } + } + + fun onDismissResourcesMenu() { + _menusState.update { it.copy(isResourcesMenuOpen = false) } + } + + fun onDismissMapMenu() { + _menusState.update { it.copy(isMapMenuOpen = false) } + } } \ No newline at end of file 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 114042b..762527e 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 @@ -79,7 +79,8 @@ data class LwaTypography( @Stable data class Debug( val title: TextStyle, - val propertyId: SpanStyle, + val propertyId: TextStyle, + val propertyIdSpan: SpanStyle, val propertyValue: TextStyle, ) } @@ -216,6 +217,11 @@ fun lwaTypography( fontWeight = FontWeight.Normal, fontSize = 10.sp, lineHeight = 14.sp, + ), + propertyIdSpan = robotoMono.caption.copy( + fontWeight = FontWeight.Normal, + fontSize = 10.sp, + lineHeight = 14.sp, ).toSpanStyle(), propertyValue = robotoMono.caption.copy( fontWeight = FontWeight.Normal, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5407a1e..3ecfbd9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] + kotlin = "2.0.21" kotlinx-coroutines = "1.9.0" kotlinx-json = "1.8.0" @@ -16,6 +17,7 @@ buildkonfig = "0.17.0" shader = "0.3.0" [plugins] + composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } @@ -25,27 +27,33 @@ ktor = { id = "io.ktor.plugin", version.ref = "ktor" } buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } [libraries] + # Test & preview kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } compose-desktop-preview = { group = "org.jetbrains.compose.ui", name = "ui-tooling-preview", version.ref = "compose-multiplatform" } + # Compose & Kotlin androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" } androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } androidx-navigation-compose = { group = "org.jetbrains.androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation" } kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-json" } + # UI. coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" } engawapg-zoomable = { module = "net.engawapg.lib:zoomable", version.ref = "zoomable" } + # Shader hypnoticcanvas = { module = "com.mikepenz.hypnoticcanvas:hypnoticcanvas", version.ref = "shader" } hypnoticcanvas-shaders = { module = "com.mikepenz.hypnoticcanvas:hypnoticcanvas-shaders", version.ref = "shader" } + # Injection with Koin koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" } koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" } koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" } + # Ktor server & client ktor-serialization-json = { group = 'io.ktor', name = 'ktor-serialization-kotlinx-json', version.ref = "ktor" } ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } @@ -56,8 +64,10 @@ ktor-server-core = { group = 'io.ktor', name = "ktor-server-core", version.ref = ktor-server-netty = { group = 'io.ktor', name = "ktor-server-netty", version.ref = "ktor" } ktor-server-websockets = { group = 'io.ktor', name = "ktor-server-websockets", version.ref = "ktor" } ktor-server-negotiation = { group = 'io.ktor', name = 'ktor-server-content-negotiation', version.ref = "ktor" } + # Utilitary run command line. turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" } + # Utilitary logging use by Ktor. logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } androidx-ui-graphics-android = { group = "androidx.compose.ui", name = "ui-graphics-android", version.ref = "ui-graphics-android" } \ No newline at end of file