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