diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_foggy_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_foggy_24dp.xml
new file mode 100644
index 0000000..1c6e5d6
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_foggy_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_foggy_filled_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_foggy_filled_24dp.xml
new file mode 100644
index 0000000..ba07634
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_foggy_filled_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
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
index 664ab2a..349665e 100644
--- 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
@@ -1,20 +1,22 @@
package com.pixelized.desktop.lwa.ui.composable.scene
-import androidx.compose.foundation.layout.Column
+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 androidx.compose.ui.unit.dp
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
@@ -29,6 +31,7 @@ fun MapScene(
) {
val campaign = LocalCampaignLayoutScope.current
val scope = rememberCoroutineScope()
+ val fogOfWarEdit = remember { mutableStateOf(true) }
val map = rememberLayoutFromResource(
name = "Dahomé",
@@ -68,7 +71,7 @@ fun MapScene(
initialZoom = 1f,
initialOffset = IntOffset(x = -150, y = -120),
),
- fogOfWar = FogOfWar.NONE,
+ fogOfWar = FogOfWar(),
layers = listOf(
map,
mapRegionOverlay,
@@ -81,14 +84,21 @@ fun MapScene(
)
}
Scene(
- modifier = modifier,
+ modifier = modifier.onFogOfWarControl(
+ scope = scope,
+ enable = fogOfWarEdit.value,
+ fogOfWar = scene.fogOfWar,
+ camera = scene.camera,
+ ),
scene = scene,
) {
- Column(
+ Row(
modifier = Modifier
- .align(alignment = Alignment.BottomEnd)
- .padding(end = campaign.rightPanel.value.width)
- .padding(all = 8.dp)
+ .align(alignment = Alignment.BottomStart)
+ .padding(
+ start = campaign.leftPanel.value.width,
+ bottom = campaign.chatOverlay.value.height,
+ )
) {
IconButton(
onClick = {
@@ -149,6 +159,19 @@ fun MapScene(
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/FogOfWar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/scene/FogOfWar.kt
index 7c77717..b8bec27 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.kt
@@ -1,13 +1,78 @@
package com.pixelized.desktop.lwa.ui.composable.scene
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.utils.global
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
@Stable
-data class FogOfWar(
- val color: Color = Color.Black.copy(alpha = 0.0f),
+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,
+ enable: Boolean,
+ camera: Camera,
+ fogOfWar: FogOfWar,
+) = if (!enable) {
+ this
+} else {
+ var lastEventTime = System.currentTimeMillis()
+ var previousEvent: PointerEvent? = null
+
+ this
+ .onPointerEvent(PointerEventType.Release) { event: PointerEvent ->
+ scope.launch {
+ println("PointerEventType.Release")
+ lastEventTime = System.currentTimeMillis()
+ previousEvent = null
+ }
+ }
+ .onPointerEvent(PointerEventType.Move) { event: PointerEvent ->
+ scope.launch {
+ val pointer = event.changes.firstOrNull()
+ val time = pointer?.uptimeMillis ?: 0L
+
+ if (time - lastEventTime > 10L && event.buttons.isPrimaryPressed) {
+ if (previousEvent?.buttons?.isPrimaryPressed == true) {
+ println("PointerEventType.LineTo")
+ pointer?.position
+ ?.global(camera = camera)
+ ?.let(fogOfWar::lineTo)
+ } else {
+ println("PointerEventType.MoveTo")
+ pointer?.position
+ ?.global(camera = camera)
+ ?.let(fogOfWar::moveTo)
+ }
+ lastEventTime = System.currentTimeMillis()
+ previousEvent = event
+ }
+ }
+ }
}
\ 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 2a72545..21e6fb8 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
@@ -15,8 +15,14 @@ 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
@@ -55,7 +61,6 @@ data class Scene(
}
-
@Composable
fun Scene(
modifier: Modifier = Modifier,
@@ -253,11 +258,40 @@ fun Modifier.drawElements(
fun Modifier.drawFogOfWar(
scene: Scene,
-): Modifier = this.drawWithCache {
- onDrawBehind {
- drawRect(color = scene.fogOfWar.color)
+): 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(),
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/SceneDebugPanel.kt
index 85a7df9..45c99b7 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/SceneDebugPanel.kt
@@ -1,14 +1,16 @@
package com.pixelized.desktop.lwa.ui.composable.scene
+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.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
@@ -30,8 +32,10 @@ fun SceneDebugPanel(
cursor: Cursor,
scene: Scene,
) {
- Card (
- modifier = modifier,
+ Card(
+ modifier = Modifier
+ .verticalScroll(state = rememberScrollState())
+ .then(other = modifier),
backgroundColor = MaterialTheme.lwa.colorScheme.elevated.base4dp,
) {
Column(
@@ -39,12 +43,15 @@ fun SceneDebugPanel(
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 {
@@ -54,7 +61,9 @@ fun SceneDebugPanel(
)
scene.layers.forEach { layer ->
SceneLayerDebug(
- modifier = Modifier.padding(start = MaterialTheme.lwa.dimen.debug.offset),
+ modifier = Modifier
+ .animateContentSize()
+ .padding(start = MaterialTheme.lwa.dimen.debug.offset),
isOpen = false,
layer = layer,
)
@@ -67,7 +76,9 @@ fun SceneDebugPanel(
)
scene.elements.forEach { element ->
SceneElementDebug(
- modifier = Modifier.padding(start = MaterialTheme.lwa.dimen.debug.offset),
+ modifier = Modifier
+ .animateContentSize()
+ .padding(start = MaterialTheme.lwa.dimen.debug.offset),
isOpen = false,
element = element,
)