Fix draggable
This commit is contained in:
parent
693f608b14
commit
8c79c913e6
@ -5,8 +5,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.PointMode
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.VectorPainter
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
@ -136,23 +136,18 @@ public class MapBitmapImageFeature(
|
||||
|
||||
public class MapVectorImageFeature(
|
||||
public val position: GeodeticMapCoordinates,
|
||||
public val painter: Painter,
|
||||
public val size: DpSize,
|
||||
public val image: ImageVector,
|
||||
public val size: DpSize = DpSize(20.dp, 20.dp),
|
||||
override val zoomRange: IntRange = defaultZoomRange,
|
||||
) : DraggableMapFeature {
|
||||
override fun getBoundingBox(zoom: Double): GmcRectangle = GmcRectangle(position, position)
|
||||
|
||||
override fun withCoordinates(newCoordinates: GeodeticMapCoordinates): MapFeature =
|
||||
MapVectorImageFeature(newCoordinates, painter, size, zoomRange)
|
||||
}
|
||||
MapVectorImageFeature(newCoordinates, image, size, zoomRange)
|
||||
|
||||
@Composable
|
||||
public fun MapVectorImageFeature(
|
||||
position: GeodeticMapCoordinates,
|
||||
image: ImageVector,
|
||||
size: DpSize = DpSize(20.dp, 20.dp),
|
||||
zoomRange: IntRange = defaultZoomRange,
|
||||
): MapVectorImageFeature = MapVectorImageFeature(position, rememberVectorPainter(image), size, zoomRange)
|
||||
@Composable
|
||||
public fun painter(): VectorPainter = rememberVectorPainter(image)
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of other features
|
||||
|
@ -38,16 +38,16 @@ public class MapFeaturesState internal constructor(
|
||||
return safeId
|
||||
}
|
||||
|
||||
public fun <T> setAttribute(id: FeatureId, key: MapFeaturesState.Attribute<T>, value: T) {
|
||||
public fun <T> setAttribute(id: FeatureId, key: Attribute<T>, value: T) {
|
||||
attributes.getOrPut(id) { mutableStateMapOf() }[key] = value
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <T> getAttribute(id: FeatureId, key: MapFeaturesState.Attribute<T>): T? =
|
||||
public fun <T> getAttribute(id: FeatureId, key: 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> {
|
||||
public fun <T> findAllWithAttribute(key: Attribute<T>, condition: (T) -> Boolean): Set<FeatureId> {
|
||||
return attributes.filterValues {
|
||||
condition(it[key] as T)
|
||||
}.keys
|
||||
@ -56,13 +56,13 @@ public class MapFeaturesState internal constructor(
|
||||
|
||||
@Composable
|
||||
public fun rememberMapFeatureState(
|
||||
builder: @Composable MapFeaturesState.() -> Unit = {},
|
||||
builder: MapFeaturesState.() -> Unit = {},
|
||||
): MapFeaturesState = remember(builder) {
|
||||
MapFeaturesState(
|
||||
mutableStateMapOf(),
|
||||
mutableStateMapOf()
|
||||
)
|
||||
}.apply { builder() }
|
||||
).apply(builder)
|
||||
}
|
||||
|
||||
public fun MapFeaturesState.circle(
|
||||
center: GeodeticMapCoordinates,
|
||||
@ -183,7 +183,6 @@ public fun MapFeaturesState.points(
|
||||
id: FeatureId? = null,
|
||||
): FeatureId = addFeature(id, MapPointsFeature(points.map { it.toCoordinates() }, zoomRange, stroke, color, pointMode))
|
||||
|
||||
@Composable
|
||||
public fun MapFeaturesState.image(
|
||||
position: Pair<Double, Double>,
|
||||
image: ImageVector,
|
||||
@ -192,13 +191,15 @@ public fun MapFeaturesState.image(
|
||||
id: FeatureId? = null,
|
||||
): FeatureId = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange))
|
||||
|
||||
@Composable
|
||||
public fun MapFeaturesState.group(
|
||||
zoomRange: IntRange = defaultZoomRange,
|
||||
id: FeatureId? = null,
|
||||
builder: @Composable MapFeaturesState.() -> Unit,
|
||||
builder: MapFeaturesState.() -> Unit,
|
||||
): FeatureId {
|
||||
val map = rememberMapFeatureState(builder).features()
|
||||
val map = MapFeaturesState(
|
||||
mutableStateMapOf(),
|
||||
mutableStateMapOf()
|
||||
).apply(builder).features()
|
||||
val feature = MapFeatureGroup(map, zoomRange)
|
||||
return addFeature(id, feature)
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ internal fun GmcRectangle.computeViewPoint(
|
||||
return MapViewPoint(center, zoom)
|
||||
}
|
||||
|
||||
private val defaultCanvasSize = DpSize(512.dp, 512.dp)
|
||||
internal val defaultCanvasSize = DpSize(512.dp, 512.dp)
|
||||
|
||||
/**
|
||||
* Draw a map using convenient parameters. If neither [initialViewPoint], noe [initialRectangle] is defined,
|
||||
@ -103,46 +103,44 @@ public fun MapView(
|
||||
initialRectangle: GmcRectangle? = null,
|
||||
config: MapViewConfig = MapViewConfig(),
|
||||
modifier: Modifier = Modifier.fillMaxSize(),
|
||||
buildFeatures: @Composable (MapFeaturesState.() -> Unit) = {},
|
||||
buildFeatures: MapFeaturesState.() -> Unit = {},
|
||||
): Unit = key(buildFeatures) {
|
||||
|
||||
val featureState = rememberMapFeatureState(buildFeatures)
|
||||
|
||||
val features = featureState.features()
|
||||
|
||||
var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) }
|
||||
|
||||
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle, cachedCanvasSize) {
|
||||
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle) {
|
||||
initialViewPoint
|
||||
?: initialRectangle?.computeViewPoint(mapTileProvider, cachedCanvasSize)
|
||||
?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, cachedCanvasSize)
|
||||
?: initialRectangle?.computeViewPoint(mapTileProvider, defaultCanvasSize)
|
||||
?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, defaultCanvasSize)
|
||||
?: MapViewPoint.globe
|
||||
}
|
||||
|
||||
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
|
||||
val featureDrag by remember {
|
||||
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 by derivedStateOf {
|
||||
config.copy(
|
||||
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
|
||||
onCanvasSizeChange = { canvasSize ->
|
||||
config.onCanvasSizeChange(canvasSize)
|
||||
cachedCanvasSize = canvasSize
|
||||
}
|
||||
)
|
||||
val newConfig by remember {
|
||||
derivedStateOf {
|
||||
config.copy(
|
||||
dragHandle = DragHandle.combine(featureDrag, config.dragHandle)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -45,7 +45,6 @@ private val logger = KotlinLogging.logger("MapView")
|
||||
/**
|
||||
* A component that renders map and provides basic map manipulation capabilities
|
||||
*/
|
||||
|
||||
@Composable
|
||||
public actual fun MapView(
|
||||
mapTileProvider: MapTileProvider,
|
||||
@ -54,10 +53,12 @@ public actual fun MapView(
|
||||
config: MapViewConfig,
|
||||
modifier: Modifier,
|
||||
): Unit = key(initialViewPoint) {
|
||||
var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
|
||||
var canvasSize by remember { mutableStateOf(defaultCanvasSize) }
|
||||
|
||||
var viewPoint by remember { mutableStateOf(initialViewPoint) }
|
||||
|
||||
require(viewPoint.zoom in 1.0..18.0) { "Zoom value of ${viewPoint.zoom} is not valid" }
|
||||
|
||||
fun setViewPoint(newViewPoint: MapViewPoint) {
|
||||
config.onViewChange(newViewPoint)
|
||||
viewPoint = newViewPoint
|
||||
@ -68,9 +69,7 @@ public actual fun MapView(
|
||||
floor(viewPoint.zoom).toInt()
|
||||
}
|
||||
|
||||
val tileScale: Double by derivedStateOf {
|
||||
2.0.pow(viewPoint.zoom - zoom)
|
||||
}
|
||||
val tileScale: Double by derivedStateOf { 2.0.pow(viewPoint.zoom - zoom) }
|
||||
|
||||
val mapTiles = remember(mapTileProvider) { mutableStateListOf<MapTile>() }
|
||||
|
||||
@ -103,9 +102,12 @@ public actual fun MapView(
|
||||
val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp())
|
||||
|
||||
//start selection
|
||||
if (event.buttons.isPrimaryPressed && event.keyboardModifiers.isShiftPressed) {
|
||||
selectRect = Rect(change.position, change.position)
|
||||
}
|
||||
var selectionStart: Offset? =
|
||||
if (event.buttons.isPrimaryPressed && event.keyboardModifiers.isShiftPressed) {
|
||||
change.position
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
drag(change.id) { dragChange ->
|
||||
val dragAmount = dragChange.position - dragChange.previousPosition
|
||||
@ -124,19 +126,19 @@ public actual fun MapView(
|
||||
)
|
||||
) {
|
||||
//clear selection just in case
|
||||
selectRect = null
|
||||
selectionStart = null
|
||||
return@drag
|
||||
}
|
||||
|
||||
if (event.buttons.isPrimaryPressed) {
|
||||
//Evaluating selection frame
|
||||
selectRect?.let { rect ->
|
||||
selectionStart?.let { start ->
|
||||
val offset = dragChange.position
|
||||
selectRect = Rect(
|
||||
min(offset.x, rect.left),
|
||||
min(offset.y, rect.top),
|
||||
max(offset.x, rect.right),
|
||||
max(offset.y, rect.bottom)
|
||||
min(offset.x, start.x),
|
||||
min(offset.y, start.y),
|
||||
max(offset.x, start.x),
|
||||
max(offset.y, start.y)
|
||||
)
|
||||
return@drag
|
||||
}
|
||||
@ -214,6 +216,8 @@ public actual fun MapView(
|
||||
}
|
||||
}
|
||||
|
||||
val painterCache = features.features().values.filterIsInstance<MapVectorImageFeature>().associateWith { it.painter() }
|
||||
|
||||
|
||||
Canvas(canvasModifier) {
|
||||
fun WebMercatorCoordinates.toOffset(): Offset = Offset(
|
||||
@ -265,7 +269,7 @@ public actual fun MapView(
|
||||
val offset = feature.position.toOffset()
|
||||
val size = feature.size.toSize()
|
||||
translate(offset.x - size.width / 2, offset.y - size.height / 2) {
|
||||
with(feature.painter) {
|
||||
with(painterCache[feature]!!) {
|
||||
draw(size)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user