Add a debug panel to the scene + MapScene + Cursor
This commit is contained in:
parent
4e013aa358
commit
ae2c3da582
13 changed files with 858 additions and 153 deletions
|
|
@ -2,15 +2,32 @@ package com.pixelized.desktop.lwa.ui.composable.scene
|
||||||
|
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.Animatable
|
||||||
import androidx.compose.animation.core.VectorConverter
|
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.Stable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
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.IntOffset
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastRoundToInt
|
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
|
@Stable
|
||||||
class Camera(
|
class Camera(
|
||||||
|
|
@ -98,4 +115,63 @@ class Camera(
|
||||||
else -> _zoom.animateTo(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())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.composable.scene
|
||||||
|
|
||||||
|
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.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.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(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
cursor: Cursor,
|
||||||
|
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 = "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())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.composable.scene
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
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.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_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 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.NONE,
|
||||||
|
layers = listOf(
|
||||||
|
map,
|
||||||
|
mapRegionOverlay,
|
||||||
|
),
|
||||||
|
elements = listOf(
|
||||||
|
element1,
|
||||||
|
element2,
|
||||||
|
element3,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Scene(
|
||||||
|
modifier = modifier,
|
||||||
|
scene = scene,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(alignment = Alignment.BottomEnd)
|
||||||
|
.padding(end = campaign.rightPanel.value.width)
|
||||||
|
.padding(all = 8.dp)
|
||||||
|
) {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,5 +5,9 @@ import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class FogOfWar(
|
data class FogOfWar(
|
||||||
val color: Color = Color.Black.copy(alpha = 0.5f),
|
val color: Color = Color.Black.copy(alpha = 0.0f),
|
||||||
)
|
) {
|
||||||
|
companion object {
|
||||||
|
val NONE = FogOfWar(color = Color.Transparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,9 @@ package com.pixelized.desktop.lwa.ui.composable.scene
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.IconButton
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Add
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -17,6 +14,9 @@ import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.drawWithCache
|
import androidx.compose.ui.draw.drawWithCache
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.pointer.PointerEvent
|
import androidx.compose.ui.input.pointer.PointerEvent
|
||||||
import androidx.compose.ui.input.pointer.PointerEventType
|
import androidx.compose.ui.input.pointer.PointerEventType
|
||||||
|
|
@ -28,163 +28,107 @@ import androidx.compose.ui.input.pointer.onPointerEvent
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.toSize
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.LocalCampaignLayoutScope
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
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_zoom_in_map_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_zoom_out_map_24dp
|
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_visibility_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.DrawableResource
|
import org.jetbrains.compose.resources.DrawableResource
|
||||||
import org.jetbrains.compose.resources.imageResource
|
import org.jetbrains.compose.resources.imageResource
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import java.util.UUID
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class Scene(
|
data class Scene(
|
||||||
val camera: Camera,
|
val camera: Camera,
|
||||||
val layouts: List<Layout>,
|
|
||||||
val fogOfWar: FogOfWar,
|
val fogOfWar: FogOfWar,
|
||||||
|
val layers: List<SceneLayer>,
|
||||||
|
val elements: List<SceneElement>,
|
||||||
) {
|
) {
|
||||||
val size: IntSize = IntSize(
|
val size: IntSize = IntSize(
|
||||||
width = layouts.maxOf { it.size.width },
|
width = layers.maxOf { it.size.width },
|
||||||
height = layouts.maxOf { it.size.height },
|
height = layers.maxOf { it.size.height },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Scene(
|
fun Scene(
|
||||||
modifier: Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
scene: Scene,
|
||||||
|
content: @Composable BoxScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
val campaign = LocalCampaignLayoutScope.current
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val scene = rememberScene(
|
val cursors = remember {
|
||||||
camera = Camera(
|
listOf(
|
||||||
initialZoom = 1f,
|
Cursor()
|
||||||
initialOffset = IntOffset(x = -150, y = -120),
|
)
|
||||||
),
|
}
|
||||||
fogOfWar = FogOfWar(),
|
|
||||||
rememberLayoutFromResource(
|
|
||||||
resource = Res.drawable.image_dahome_maps,
|
|
||||||
),
|
|
||||||
rememberLayoutFromResource(
|
|
||||||
resource = Res.drawable.image_dahome_regions,
|
|
||||||
),
|
|
||||||
rememberLayoutFromResource(
|
|
||||||
resource = Res.drawable.icon_d100,
|
|
||||||
offset = IntOffset(x = 1740, y = 910),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.graphicsLayer { clip = true }
|
.graphicsLayer { clip = true }
|
||||||
.onCameraControl(scope = scope, scene = scene)
|
.onCameraControl(scope = scope, scene = scene)
|
||||||
.drawScene(scene = scene)
|
.onCursorControl(camera = scene.camera, cursor = cursors.first())
|
||||||
.fogOfWar(scene = scene)
|
.drawLayers(camera = scene.camera, layers = scene.layers)
|
||||||
|
.drawElements(camera = scene.camera, elements = scene.elements)
|
||||||
|
.drawCursors(camera = scene.camera, cursors = cursors)
|
||||||
|
.drawFogOfWar(scene = scene),
|
||||||
) {
|
) {
|
||||||
Column(
|
content()
|
||||||
modifier = Modifier
|
|
||||||
.align(alignment = Alignment.BottomEnd)
|
|
||||||
.padding(end = campaign.rightPanel.value.width)
|
|
||||||
.padding(all = 8.dp)
|
|
||||||
) {
|
|
||||||
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.layouts.getOrNull(1)?.let {
|
|
||||||
it.alpha(alpha = if (it.alpha == 0f) 1f else 0f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_visibility_24dp),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
SceneDebugPanel(
|
||||||
@Stable
|
modifier = Modifier
|
||||||
fun rememberLayoutFromResource(
|
.align(alignment = Alignment.TopEnd)
|
||||||
resource: DrawableResource,
|
.padding(paddingValues = MaterialTheme.lwa.dimen.paddingValues),
|
||||||
offset: IntOffset = IntOffset.Zero,
|
cursor = cursors.first(),
|
||||||
): Layout {
|
scene = scene,
|
||||||
val texture = imageResource(
|
|
||||||
resource = resource,
|
|
||||||
)
|
|
||||||
return remember(resource) {
|
|
||||||
Layout(
|
|
||||||
texture = texture,
|
|
||||||
offset = offset,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
fun rememberScene(
|
fun rememberLayoutFromResource(
|
||||||
camera: Camera,
|
name: String,
|
||||||
fogOfWar: FogOfWar,
|
resource: DrawableResource,
|
||||||
vararg layouts: Layout,
|
offset: IntOffset = IntOffset.Zero,
|
||||||
): Scene {
|
): SceneLayer {
|
||||||
return remember {
|
val texture = imageResource(
|
||||||
Scene(
|
resource = resource,
|
||||||
camera = camera,
|
)
|
||||||
layouts = layouts.toList(),
|
return remember(resource) {
|
||||||
fogOfWar = fogOfWar,
|
SceneLayer(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
name = name,
|
||||||
|
texture = texture,
|
||||||
|
offset = offset,
|
||||||
|
alpha = 1f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun rememberElementFromResource(
|
||||||
|
name: String,
|
||||||
|
resource: DrawableResource,
|
||||||
|
offset: IntOffset = IntOffset.Zero,
|
||||||
|
): SceneElement {
|
||||||
|
val texture = imageResource(
|
||||||
|
resource = resource,
|
||||||
|
)
|
||||||
|
return remember(resource) {
|
||||||
|
SceneElement(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
name = name,
|
||||||
|
texture = texture,
|
||||||
|
offset = offset,
|
||||||
|
alpha = 1f,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -233,23 +177,81 @@ fun Modifier.onCameraControl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Modifier.drawScene(
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
scene: Scene,
|
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<Cursor>,
|
||||||
|
): 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,
|
||||||
|
layers: List<SceneLayer>,
|
||||||
): Modifier = this.drawWithCache {
|
): Modifier = this.drawWithCache {
|
||||||
onDrawBehind {
|
onDrawBehind {
|
||||||
scene.layouts.forEach { layout ->
|
layers.forEach { layers ->
|
||||||
drawImage(
|
drawImage(
|
||||||
image = layout.texture,
|
image = layers.texture,
|
||||||
srcOffset = scene.camera.offset - layout.offset,
|
srcOffset = camera.offset - layers.offset,
|
||||||
srcSize = scene.camera.cameraSizeZoomed,
|
srcSize = camera.cameraSizeZoomed,
|
||||||
dstSize = scene.camera.cameraSize,
|
dstSize = camera.cameraSize,
|
||||||
alpha = layout.alpha,
|
alpha = layers.alpha,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Modifier.fogOfWar(
|
fun Modifier.drawElements(
|
||||||
|
camera: Camera,
|
||||||
|
elements: List<SceneElement>,
|
||||||
|
): Modifier = this.drawWithCache {
|
||||||
|
onDrawBehind {
|
||||||
|
elements.forEach { element ->
|
||||||
|
drawImage(
|
||||||
|
image = element.texture,
|
||||||
|
srcOffset = camera.offset - element.position,
|
||||||
|
srcSize = camera.cameraSizeZoomed,
|
||||||
|
dstSize = camera.cameraSize,
|
||||||
|
alpha = element.alpha,
|
||||||
|
)
|
||||||
|
drawRect(
|
||||||
|
color = Color.Red,
|
||||||
|
topLeft = element.position.local(camera = camera),
|
||||||
|
size = (element.size).toSize() / camera.zoom,
|
||||||
|
style = Stroke(width = 2f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Modifier.drawFogOfWar(
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
): Modifier = this.drawWithCache {
|
): Modifier = this.drawWithCache {
|
||||||
onDrawBehind {
|
onDrawBehind {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.composable.scene
|
||||||
|
|
||||||
|
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.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
|
||||||
|
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.drawables.SceneElementDebug
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneLayerDebug
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
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,
|
||||||
|
backgroundColor = MaterialTheme.lwa.colorScheme.elevated.base4dp,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||||
|
) {
|
||||||
|
SceneDebug(
|
||||||
|
scene = scene,
|
||||||
|
)
|
||||||
|
SceneCursorDebug(
|
||||||
|
cursor = cursor,
|
||||||
|
)
|
||||||
|
SceneCameraDebug(
|
||||||
|
camera = scene.camera,
|
||||||
|
)
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.lwa.typography.debug.title,
|
||||||
|
text = "Layers:(${scene.layers.size})"
|
||||||
|
)
|
||||||
|
scene.layers.forEach { layer ->
|
||||||
|
SceneLayerDebug(
|
||||||
|
modifier = Modifier.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.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,
|
||||||
|
) {
|
||||||
|
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 = "Scene",
|
||||||
|
)
|
||||||
|
if (isOpen.value) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("Size: ") }
|
||||||
|
append(scene.size.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
package com.pixelized.desktop.lwa.ui.composable.scene
|
package com.pixelized.desktop.lwa.ui.composable.scene.drawables
|
||||||
|
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.Animatable
|
||||||
import androidx.compose.animation.core.VectorConverter
|
import androidx.compose.animation.core.VectorConverter
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
|
||||||
@Stable
|
open class SceneDrawable(
|
||||||
class Layout(
|
val id: String,
|
||||||
|
val name: String,
|
||||||
val texture: ImageBitmap,
|
val texture: ImageBitmap,
|
||||||
val offset: IntOffset = IntOffset.Zero,
|
val offset: IntOffset,
|
||||||
val size: IntSize = IntSize(texture.width, texture.height),
|
val size: IntSize,
|
||||||
private val initialAlpha: Float = 1f,
|
private val initialAlpha: Float,
|
||||||
) {
|
) {
|
||||||
private val _alpha = Animatable(
|
private val _alpha = Animatable(
|
||||||
initialValue = initialAlpha,
|
initialValue = initialAlpha,
|
||||||
typeConverter = Float.VectorConverter,
|
typeConverter = Float.Companion.VectorConverter,
|
||||||
)
|
)
|
||||||
val alpha get() = _alpha.value
|
val alpha get() = _alpha.value
|
||||||
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.composable.scene.drawables
|
||||||
|
|
||||||
|
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.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
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 com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class SceneElement(
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
texture: ImageBitmap,
|
||||||
|
offset: IntOffset = IntOffset.Companion.Zero,
|
||||||
|
size: IntSize = IntSize(texture.width, texture.height),
|
||||||
|
alpha: Float = 1f,
|
||||||
|
) : SceneDrawable(
|
||||||
|
id = id,
|
||||||
|
name = name,
|
||||||
|
texture = texture,
|
||||||
|
offset = offset,
|
||||||
|
size = size,
|
||||||
|
initialAlpha = alpha,
|
||||||
|
) {
|
||||||
|
val position = IntOffset(
|
||||||
|
x = offset.x - size.width / 2,
|
||||||
|
y = offset.y - size.height / 2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SceneElementDebug(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
element: SceneElement,
|
||||||
|
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 = element.name,
|
||||||
|
)
|
||||||
|
if (isOpen.value) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("id: ") }
|
||||||
|
append(element.id)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("offset: ") }
|
||||||
|
append(element.offset.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("size: ") }
|
||||||
|
append(element.size.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("alpha: ") }
|
||||||
|
append(element.alpha.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.composable.scene.drawables
|
||||||
|
|
||||||
|
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.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
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 com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.typography.LwaTypography
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class SceneLayer(
|
||||||
|
id : String,
|
||||||
|
name: String,
|
||||||
|
texture: ImageBitmap,
|
||||||
|
offset: IntOffset = IntOffset.Companion.Zero,
|
||||||
|
size: IntSize = IntSize(texture.width, texture.height),
|
||||||
|
alpha: Float = 1f,
|
||||||
|
) : SceneDrawable(
|
||||||
|
id = id,
|
||||||
|
name = name,
|
||||||
|
texture = texture,
|
||||||
|
offset = offset,
|
||||||
|
size = size,
|
||||||
|
initialAlpha = alpha,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SceneLayerDebug(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
layer: SceneLayer,
|
||||||
|
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 = layer.name,
|
||||||
|
)
|
||||||
|
if (isOpen.value) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("id: ") }
|
||||||
|
append(layer.id)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("offset: ") }
|
||||||
|
append(layer.offset.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("size: ") }
|
||||||
|
append(layer.size.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(start = padding),
|
||||||
|
style = MaterialTheme.lwa.typography.debug.propertyValue,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style.propertyId) { append("alpha: ") }
|
||||||
|
append(layer.alpha.toString())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert local positon to global one.
|
||||||
|
* Global position are agnostic from camera, and therefor should be use to position stuff on the map.
|
||||||
|
* A common use case is to share players cursor.
|
||||||
|
*/
|
||||||
|
fun Offset.global(
|
||||||
|
camera: Camera,
|
||||||
|
): Offset = Offset(
|
||||||
|
x = this.x * camera.zoom + camera.offset.x,
|
||||||
|
y = this.y * camera.zoom + camera.offset.y,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert global positon to local one.
|
||||||
|
* Local position take into account the camera and are use to display stuff on the Scene composable.
|
||||||
|
*/
|
||||||
|
fun Offset.local(
|
||||||
|
camera: Camera,
|
||||||
|
): Offset = Offset(
|
||||||
|
x = (this.x - camera.offset.x) / camera.zoom,
|
||||||
|
y = (this.y - camera.offset.y) / camera.zoom,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert global positon to local one.
|
||||||
|
* Local position take into account the camera and are use to display stuff on the Scene composable.
|
||||||
|
*/
|
||||||
|
fun IntOffset.local(
|
||||||
|
camera: Camera,
|
||||||
|
): Offset = Offset(
|
||||||
|
x = (this.x.toFloat() - camera.offset.x) / camera.zoom,
|
||||||
|
y = (this.y.toFloat() - camera.offset.y) / camera.zoom,
|
||||||
|
)
|
||||||
|
|
@ -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.character.diminished.CharacterSheetDiminishedDialogViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
|
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.key.KeyHandler
|
||||||
import com.pixelized.desktop.lwa.ui.composable.scene.Scene
|
import com.pixelized.desktop.lwa.ui.composable.scene.MapScene
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
|
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
|
||||||
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToLevelScreen
|
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToLevelScreen
|
||||||
import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlay
|
import com.pixelized.desktop.lwa.ui.overlay.portrait.PortraitOverlay
|
||||||
|
|
@ -101,7 +101,7 @@ fun CampaignScreen(
|
||||||
|
|
||||||
},
|
},
|
||||||
main = {
|
main = {
|
||||||
Scene(
|
MapScene(
|
||||||
modifier = Modifier.matchParentSize(),
|
modifier = Modifier.matchParentSize(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ data class LwaDimen(
|
||||||
val layout: Layout,
|
val layout: Layout,
|
||||||
val portrait: Portrait,
|
val portrait: Portrait,
|
||||||
val sheet: Sheet,
|
val sheet: Sheet,
|
||||||
|
val debug: Debug,
|
||||||
) {
|
) {
|
||||||
@Stable
|
@Stable
|
||||||
data class Layout(
|
data class Layout(
|
||||||
|
|
@ -33,6 +34,12 @@ data class LwaDimen(
|
||||||
val subCategory: Dp,
|
val subCategory: Dp,
|
||||||
val characteristic: DpSize,
|
val characteristic: DpSize,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class Debug(
|
||||||
|
val panelWidth: Dp,
|
||||||
|
val offset: Dp,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -52,6 +59,10 @@ fun lwaDimen(
|
||||||
detailWidth = 128.dp * 4,
|
detailWidth = 128.dp * 4,
|
||||||
chatMaxWidth = 600.dp,
|
chatMaxWidth = 600.dp,
|
||||||
),
|
),
|
||||||
|
debug: LwaDimen.Debug = LwaDimen.Debug(
|
||||||
|
panelWidth = 200.dp,
|
||||||
|
offset = 8.dp,
|
||||||
|
)
|
||||||
): LwaDimen {
|
): LwaDimen {
|
||||||
return remember {
|
return remember {
|
||||||
LwaDimen(
|
LwaDimen(
|
||||||
|
|
@ -60,6 +71,7 @@ fun lwaDimen(
|
||||||
portrait = portrait,
|
portrait = portrait,
|
||||||
sheet = sheet,
|
sheet = sheet,
|
||||||
layout = layout,
|
layout = layout,
|
||||||
|
debug = debug,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.ui.graphics.Shadow
|
||||||
import androidx.compose.ui.graphics.StrokeJoin
|
import androidx.compose.ui.graphics.StrokeJoin
|
||||||
import androidx.compose.ui.graphics.drawscope.Fill
|
import androidx.compose.ui.graphics.drawscope.Fill
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
|
@ -30,6 +31,7 @@ data class LwaTypography(
|
||||||
val characterSheet: CharacterSheet,
|
val characterSheet: CharacterSheet,
|
||||||
val inventory: Inventory,
|
val inventory: Inventory,
|
||||||
val freeDiceThrow: Dice,
|
val freeDiceThrow: Dice,
|
||||||
|
val debug: Debug,
|
||||||
) {
|
) {
|
||||||
@Stable
|
@Stable
|
||||||
data class Chat(
|
data class Chat(
|
||||||
|
|
@ -73,6 +75,13 @@ data class LwaTypography(
|
||||||
val dice: TextStyle,
|
val dice: TextStyle,
|
||||||
val result: TextStyle,
|
val result: TextStyle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class Debug(
|
||||||
|
val title: TextStyle,
|
||||||
|
val propertyId: SpanStyle,
|
||||||
|
val propertyValue: TextStyle,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -196,7 +205,24 @@ fun lwaTypography(
|
||||||
result = robotoMono.h4.copy(
|
result = robotoMono.h4.copy(
|
||||||
color = colors.base.onSurface,
|
color = colors.base.onSurface,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
debug = LwaTypography.Debug(
|
||||||
|
title = robotoMono.caption.copy(
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
lineHeight = 14.sp,
|
||||||
|
),
|
||||||
|
propertyId = robotoMono.caption.copy(
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 10.sp,
|
||||||
|
lineHeight = 14.sp,
|
||||||
|
).toSpanStyle(),
|
||||||
|
propertyValue = robotoMono.caption.copy(
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
lineHeight = 14.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue