Change state propagation to better support dynamic features. Drag not working for now

This commit is contained in:
Alexander Nozik 2022-09-13 20:30:49 +03:00
parent fa51fc03db
commit 4c71b8e7bb
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
3 changed files with 40 additions and 48 deletions

View File

@ -28,21 +28,16 @@ public interface MapFeatureBuilder {
public fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T)
public val features: MutableMap<FeatureId, MapFeature>
public val attributes: Map<FeatureId, MapFeatureAttributeSet>
//TODO use context receiver for that
public fun FeatureId.draggable(enabled: Boolean = true) {
setAttribute(this, DraggableAttribute, enabled)
}
}
internal class MapFeatureBuilderImpl(
override val features: SnapshotStateMap<FeatureId, MapFeature>,
) : MapFeatureBuilder {
internal class MapFeatureBuilderImpl: MapFeatureBuilder {
private val _attributes = SnapshotStateMap<FeatureId, SnapshotStateMap<MapFeatureAttributeKey<out Any?>, in Any?>>()
internal val features = mutableStateMapOf<FeatureId, MapFeature>()
internal val attributes = SnapshotStateMap<FeatureId, SnapshotStateMap<MapFeatureAttributeKey<out Any?>, in Any?>>()
private fun generateID(feature: MapFeature): FeatureId = "@feature[${feature.hashCode().toUInt()}]"
@ -54,14 +49,13 @@ internal class MapFeatureBuilderImpl(
}
override fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T) {
_attributes.getOrPut(id) { SnapshotStateMap() }[key] = value
attributes.getOrPut(id) { SnapshotStateMap() }[key] = value
}
override val attributes: Map<FeatureId, MapFeatureAttributeSet>
get() = _attributes.mapValues { MapFeatureAttributeSet(it.value) }
}
public fun MapFeatureBuilder.circle(
center: GeodeticMapCoordinates,
zoomRange: IntRange = defaultZoomRange,
@ -195,7 +189,7 @@ public fun MapFeatureBuilder.group(
id: FeatureId? = null,
builder: MapFeatureBuilder.() -> Unit,
): FeatureId {
val map = MapFeatureBuilderImpl(mutableStateMapOf()).apply(builder).features
val map = MapFeatureBuilderImpl().apply(builder).features
val feature = MapFeatureGroup(map, zoomRange)
return addFeature(id, feature)
}

View File

@ -2,11 +2,11 @@ package center.sciprog.maps.compose
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.isPrimaryPressed
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import center.sciprog.maps.coordinates.*
import kotlin.math.PI
import kotlin.math.log2
@ -87,6 +87,8 @@ internal fun GmcRectangle.computeViewPoint(
return MapViewPoint(center, zoom)
}
private val defaultCanvasSize = DpSize(512.dp, 512.dp)
/**
* Draw a map using convenient parameters. If neither [initialViewPoint], noe [initialRectangle] is defined,
* use map features to infer view region.
@ -102,46 +104,48 @@ public fun MapView(
config: MapViewConfig = MapViewConfig(),
modifier: Modifier = Modifier.fillMaxSize(),
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
) {
): Unit = key(buildFeatures) {
var viewPointOverride by remember(initialViewPoint, initialRectangle) { mutableStateOf(initialViewPoint ?: MapViewPoint.globe) }
val featuresBuilder = MapFeatureBuilderImpl().apply { buildFeatures() }
val featuresBuilder = MapFeatureBuilderImpl(mutableStateMapOf()).apply { buildFeatures() }
val features = featuresBuilder.features
val attributes = featuresBuilder.attributes
val features: SnapshotStateMap<FeatureId, MapFeature> = remember(buildFeatures) { featuresBuilder.features }
var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) }
val attributes = remember(buildFeatures) { featuresBuilder.attributes }
val featureDrag by derivedStateOf {
DragHandle.withPrimaryButton { _, start, end ->
val zoom = start.zoom
attributes.filterValues {
it[DraggableAttribute] ?: false
}.keys.forEach { id ->
val feature = features[id] as? DraggableMapFeature ?: return@forEach
//val border = WebMercatorProjection.scaleFactor(zoom)
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
if (start.focus in boundingBox) {
features[id] = feature.withCoordinates(end.focus)
return@withPrimaryButton false
}
}
return@withPrimaryButton true
}
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle, cachedCanvasSize) {
initialViewPoint
?: initialRectangle?.computeViewPoint(mapTileProvider, cachedCanvasSize)
?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, cachedCanvasSize)
?: MapViewPoint.globe
}
val featureDrag = DragHandle.withPrimaryButton { _, start, end ->
val zoom = start.zoom
attributes.filterValues {
it[DraggableAttribute] as? Boolean ?: false
}.keys.forEach { id ->
val feature = features[id] as? DraggableMapFeature ?: return@forEach
//val border = WebMercatorProjection.scaleFactor(zoom)
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
if (start.focus in boundingBox) {
features[id] = feature.withCoordinates(end.focus)
return@withPrimaryButton false
}
}
return@withPrimaryButton true
}
val newConfig = config.copy(
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
onCanvasSizeChange = { canvasSize ->
viewPointOverride = initialViewPoint
?: initialRectangle?.computeViewPoint(mapTileProvider, canvasSize)
?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, canvasSize)
?: MapViewPoint.globe
config.onCanvasSizeChange(canvasSize)
cachedCanvasSize = canvasSize
}
)
MapView(
mapTileProvider = mapTileProvider,
initialViewPoint = viewPointOverride,

View File

@ -53,17 +53,11 @@ public actual fun MapView(
features: Map<FeatureId, MapFeature>,
config: MapViewConfig,
modifier: Modifier,
) {
): Unit = key(initialViewPoint) {
var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
var viewPointOverride by remember { mutableStateOf(initialViewPoint) }
var viewPoint by remember { mutableStateOf(initialViewPoint) }
if (viewPointOverride != initialViewPoint) {
viewPoint = initialViewPoint
viewPointOverride = initialViewPoint
}
fun setViewPoint(newViewPoint: MapViewPoint) {
config.onViewChange(newViewPoint)
viewPoint = newViewPoint
@ -78,7 +72,7 @@ public actual fun MapView(
2.0.pow(viewPoint.zoom - zoom)
}
val mapTiles = remember { mutableStateListOf<MapTile>() }
val mapTiles = remember(mapTileProvider) { mutableStateListOf<MapTile>() }
val centerCoordinates by derivedStateOf { WebMercatorProjection.toMercator(viewPoint.focus, zoom) }