Reorder some API

This commit is contained in:
Alexander Nozik 2022-09-13 22:53:56 +03:00
parent 4c71b8e7bb
commit 693f608b14
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
4 changed files with 74 additions and 68 deletions

View File

@ -2,6 +2,7 @@ package center.sciprog.maps.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PointMode
@ -13,50 +14,57 @@ import center.sciprog.maps.coordinates.*
public typealias FeatureId = String
public interface MapFeatureAttributeKey<T>
public object DraggableAttribute : MapFeaturesState.Attribute<Boolean>
public class MapFeatureAttributeSet(private val map: Map<MapFeatureAttributeKey<*>, *>) {
public operator fun <T> get(key: MapFeatureAttributeKey<*>): T? = map[key]?.let {
@Suppress("UNCHECKED_CAST")
it as T
}
}
public interface MapFeatureBuilder {
public fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId
public fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T)
public class MapFeaturesState internal constructor(
private val features: MutableMap<FeatureId, MapFeature>,
private val attributes: MutableMap<FeatureId, SnapshotStateMap<Attribute<out Any?>, in Any?>>,
) {
public interface Attribute<T>
//TODO use context receiver for that
public fun FeatureId.draggable(enabled: Boolean = true) {
setAttribute(this, DraggableAttribute, enabled)
}
}
internal class MapFeatureBuilderImpl: MapFeatureBuilder {
internal val features = mutableStateMapOf<FeatureId, MapFeature>()
internal val attributes = SnapshotStateMap<FeatureId, SnapshotStateMap<MapFeatureAttributeKey<out Any?>, in Any?>>()
public fun features(): Map<FeatureId, MapFeature> = features
private fun generateID(feature: MapFeature): FeatureId = "@feature[${feature.hashCode().toUInt()}]"
override fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId {
public fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId {
val safeId = id ?: generateID(feature)
features[id ?: generateID(feature)] = feature
return safeId
}
override fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T) {
attributes.getOrPut(id) { SnapshotStateMap() }[key] = value
public fun <T> setAttribute(id: FeatureId, key: MapFeaturesState.Attribute<T>, value: T) {
attributes.getOrPut(id) { mutableStateMapOf() }[key] = value
}
@Suppress("UNCHECKED_CAST")
public fun <T> getAttribute(id: FeatureId, key: MapFeaturesState.Attribute<T>): T? =
attributes[id]?.get(key)?.let { it as T }
@Suppress("UNCHECKED_CAST")
public fun <T> findAllWithAttribute(key: MapFeaturesState.Attribute<T>, condition: (T) -> Boolean): Set<FeatureId> {
return attributes.filterValues {
condition(it[key] as T)
}.keys
}
}
@Composable
public fun rememberMapFeatureState(
builder: @Composable MapFeaturesState.() -> Unit = {},
): MapFeaturesState = remember(builder) {
MapFeaturesState(
mutableStateMapOf(),
mutableStateMapOf()
)
}.apply { builder() }
public fun MapFeatureBuilder.circle(
public fun MapFeaturesState.circle(
center: GeodeticMapCoordinates,
zoomRange: IntRange = defaultZoomRange,
size: Float = 5f,
@ -66,7 +74,7 @@ public fun MapFeatureBuilder.circle(
id, MapCircleFeature(center, zoomRange, size, color)
)
public fun MapFeatureBuilder.circle(
public fun MapFeaturesState.circle(
centerCoordinates: Pair<Double, Double>,
zoomRange: IntRange = defaultZoomRange,
size: Float = 5f,
@ -76,7 +84,7 @@ public fun MapFeatureBuilder.circle(
id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
)
public fun MapFeatureBuilder.rectangle(
public fun MapFeaturesState.rectangle(
centerCoordinates: Pair<Double, Double>,
zoomRange: IntRange = defaultZoomRange,
size: DpSize = DpSize(5.dp, 5.dp),
@ -86,14 +94,14 @@ public fun MapFeatureBuilder.rectangle(
id, MapRectangleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
)
public fun MapFeatureBuilder.draw(
public fun MapFeaturesState.draw(
position: Pair<Double, Double>,
zoomRange: IntRange = defaultZoomRange,
id: FeatureId? = null,
drawFeature: DrawScope.() -> Unit,
): FeatureId = addFeature(id, MapDrawFeature(position.toCoordinates(), zoomRange, drawFeature))
public fun MapFeatureBuilder.line(
public fun MapFeaturesState.line(
aCoordinates: Gmc,
bCoordinates: Gmc,
zoomRange: IntRange = defaultZoomRange,
@ -104,7 +112,7 @@ public fun MapFeatureBuilder.line(
MapLineFeature(aCoordinates, bCoordinates, zoomRange, color)
)
public fun MapFeatureBuilder.line(
public fun MapFeaturesState.line(
curve: GmcCurve,
zoomRange: IntRange = defaultZoomRange,
color: Color = Color.Red,
@ -114,7 +122,7 @@ public fun MapFeatureBuilder.line(
MapLineFeature(curve.forward.coordinates, curve.backward.coordinates, zoomRange, color)
)
public fun MapFeatureBuilder.line(
public fun MapFeaturesState.line(
aCoordinates: Pair<Double, Double>,
bCoordinates: Pair<Double, Double>,
zoomRange: IntRange = defaultZoomRange,
@ -125,7 +133,7 @@ public fun MapFeatureBuilder.line(
MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color)
)
public fun MapFeatureBuilder.arc(
public fun MapFeaturesState.arc(
oval: GmcRectangle,
startAngle: Angle,
arcLength: Angle,
@ -137,7 +145,7 @@ public fun MapFeatureBuilder.arc(
MapArcFeature(oval, startAngle, arcLength, zoomRange, color)
)
public fun MapFeatureBuilder.arc(
public fun MapFeaturesState.arc(
center: Pair<Double, Double>,
radius: Distance,
startAngle: Angle,
@ -156,7 +164,7 @@ public fun MapFeatureBuilder.arc(
)
)
public fun MapFeatureBuilder.points(
public fun MapFeaturesState.points(
points: List<Gmc>,
zoomRange: IntRange = defaultZoomRange,
stroke: Float = 2f,
@ -166,7 +174,7 @@ public fun MapFeatureBuilder.points(
): FeatureId = addFeature(id, MapPointsFeature(points, zoomRange, stroke, color, pointMode))
@JvmName("pointsFromPairs")
public fun MapFeatureBuilder.points(
public fun MapFeaturesState.points(
points: List<Pair<Double, Double>>,
zoomRange: IntRange = defaultZoomRange,
stroke: Float = 2f,
@ -176,7 +184,7 @@ public fun MapFeatureBuilder.points(
): FeatureId = addFeature(id, MapPointsFeature(points.map { it.toCoordinates() }, zoomRange, stroke, color, pointMode))
@Composable
public fun MapFeatureBuilder.image(
public fun MapFeaturesState.image(
position: Pair<Double, Double>,
image: ImageVector,
size: DpSize = DpSize(20.dp, 20.dp),
@ -184,17 +192,18 @@ public fun MapFeatureBuilder.image(
id: FeatureId? = null,
): FeatureId = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange))
public fun MapFeatureBuilder.group(
@Composable
public fun MapFeaturesState.group(
zoomRange: IntRange = defaultZoomRange,
id: FeatureId? = null,
builder: MapFeatureBuilder.() -> Unit,
builder: @Composable MapFeaturesState.() -> Unit,
): FeatureId {
val map = MapFeatureBuilderImpl().apply(builder).features
val map = rememberMapFeatureState(builder).features()
val feature = MapFeatureGroup(map, zoomRange)
return addFeature(id, feature)
}
public fun MapFeatureBuilder.text(
public fun MapFeaturesState.text(
position: GeodeticMapCoordinates,
text: String,
zoomRange: IntRange = defaultZoomRange,
@ -203,7 +212,7 @@ public fun MapFeatureBuilder.text(
id: FeatureId? = null,
): FeatureId = addFeature(id, MapTextFeature(position, text, zoomRange, color, font))
public fun MapFeatureBuilder.text(
public fun MapFeaturesState.text(
position: Pair<Double, Double>,
text: String,
zoomRange: IntRange = defaultZoomRange,

View File

@ -68,7 +68,7 @@ public data class MapViewConfig(
public expect fun MapView(
mapTileProvider: MapTileProvider,
initialViewPoint: MapViewPoint,
features: Map<FeatureId, MapFeature>,
features: MapFeaturesState,
config: MapViewConfig = MapViewConfig(),
modifier: Modifier = Modifier.fillMaxSize(),
)
@ -103,13 +103,12 @@ public fun MapView(
initialRectangle: GmcRectangle? = null,
config: MapViewConfig = MapViewConfig(),
modifier: Modifier = Modifier.fillMaxSize(),
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
buildFeatures: @Composable (MapFeaturesState.() -> Unit) = {},
): Unit = key(buildFeatures) {
val featuresBuilder = MapFeatureBuilderImpl().apply { buildFeatures() }
val featureState = rememberMapFeatureState(buildFeatures)
val features = featuresBuilder.features
val attributes = featuresBuilder.attributes
val features = featureState.features()
var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) }
@ -120,36 +119,37 @@ public fun MapView(
?: 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
val featureDrag by derivedStateOf {
DragHandle.withPrimaryButton { _, start, end ->
val zoom = start.zoom
featureState.findAllWithAttribute(DraggableAttribute) { it }.forEach { id ->
val feature = features[id] as? DraggableMapFeature ?: return@forEach
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
if (start.focus in boundingBox) {
featureState.addFeature(id, feature.withCoordinates(end.focus))
return@withPrimaryButton false
}
}
return@withPrimaryButton true
}
return@withPrimaryButton true
}
val newConfig = config.copy(
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
onCanvasSizeChange = { canvasSize ->
config.onCanvasSizeChange(canvasSize)
cachedCanvasSize = canvasSize
}
)
val newConfig by derivedStateOf {
config.copy(
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
onCanvasSizeChange = { canvasSize ->
config.onCanvasSizeChange(canvasSize)
cachedCanvasSize = canvasSize
}
)
}
MapView(
mapTileProvider = mapTileProvider,
initialViewPoint = viewPointOverride,
features = features,
features = featureState,
config = newConfig,
modifier = modifier,
)

View File

@ -1,3 +0,0 @@
package center.sciprog.maps.compose
public object DraggableAttribute: MapFeatureAttributeKey<Boolean>

View File

@ -50,7 +50,7 @@ private val logger = KotlinLogging.logger("MapView")
public actual fun MapView(
mapTileProvider: MapTileProvider,
initialViewPoint: MapViewPoint,
features: Map<FeatureId, MapFeature>,
features: MapFeaturesState,
config: MapViewConfig,
modifier: Modifier,
): Unit = key(initialViewPoint) {
@ -334,7 +334,7 @@ public actual fun MapView(
dstSize = tileSize
)
}
features.values.filter { zoom in it.zoomRange }.forEach { feature ->
features.features().values.filter { zoom in it.zoomRange }.forEach { feature ->
drawFeature(zoom, feature)
}
}