Fixed multi-drag issue. Added demo for bounded drag.

This commit is contained in:
Alexander Nozik 2022-12-07 18:02:20 +03:00
parent 5561a4188c
commit 5448929d31
5 changed files with 52 additions and 19 deletions

View File

@ -45,8 +45,6 @@ fun App() {
val pointTwo = 55.929444 to 37.518434 val pointTwo = 55.929444 to 37.518434
val pointThree = 60.929444 to 37.518434 val pointThree = 60.929444 to 37.518434
val dragPoint = 55.744 to 37.614
MapView( MapView(
mapTileProvider = mapTileProvider, mapTileProvider = mapTileProvider,
// initialViewPoint = MapViewPoint( // initialViewPoint = MapViewPoint(
@ -65,7 +63,27 @@ fun App() {
image(pointOne, Icons.Filled.Home) image(pointOne, Icons.Filled.Home)
rectangle(dragPoint, id = "dragMe", size = DpSize(10.dp, 10.dp)).draggable() var drag1 = Gmc.ofDegrees(55.744, 37.614)
var drag2 = Gmc.ofDegrees(55.8, 37.5)
fun updateLine() {
line(drag1, drag2, id = "connection", color = Color.Magenta)
}
rectangle(drag1, size = DpSize(10.dp, 10.dp)).draggable { _, _, end ->
drag1 = end.focus
updateLine()
true
}
rectangle(drag2, size = DpSize(10.dp, 10.dp)).draggable { _, _, end ->
drag2 = end.focus
updateLine()
true
}
updateLine()
points( points(
points = listOf( points = listOf(

View File

@ -18,10 +18,15 @@ import kotlin.math.floor
public interface MapFeature { public interface MapFeature {
public val zoomRange: IntRange public val zoomRange: IntRange
public fun getBoundingBox(zoom: Double): GmcRectangle? public fun getBoundingBox(zoom: Double): GmcRectangle?
} }
public interface DraggableMapFeature : MapFeature { public interface SelectableMapFeature : MapFeature {
public operator fun contains(point: MapViewPoint): Boolean = getBoundingBox(point.zoom)?.let {
point.focus in it
} ?: false
}
public interface DraggableMapFeature : SelectableMapFeature {
public fun withCoordinates(newCoordinates: GeodeticMapCoordinates): MapFeature public fun withCoordinates(newCoordinates: GeodeticMapCoordinates): MapFeature
} }
@ -118,8 +123,12 @@ public class MapLineFeature(
public val b: GeodeticMapCoordinates, public val b: GeodeticMapCoordinates,
override val zoomRange: IntRange = defaultZoomRange, override val zoomRange: IntRange = defaultZoomRange,
public val color: Color = Color.Red, public val color: Color = Color.Red,
) : MapFeature { ) : SelectableMapFeature {
override fun getBoundingBox(zoom: Double): GmcRectangle = GmcRectangle(a, b) override fun getBoundingBox(zoom: Double): GmcRectangle = GmcRectangle(a, b)
override fun contains(point: MapViewPoint): Boolean {
return super.contains(point)
}
} }
/** /**

View File

@ -18,14 +18,14 @@ public object DraggableAttribute : MapFeaturesState.Attribute<DragHandle>
public class MapFeaturesState internal constructor( public class MapFeaturesState internal constructor(
private val features: MutableMap<FeatureId, MapFeature>, private val features: MutableMap<FeatureId, MapFeature>,
private val attributes: MutableMap<FeatureId, SnapshotStateMap<Attribute<out Any?>, in Any?>>, @PublishedApi internal val attributes: MutableMap<FeatureId, SnapshotStateMap<Attribute<out Any?>, in Any?>>,
) { ) {
public interface Attribute<T> public interface Attribute<T>
//TODO use context receiver for that //TODO use context receiver for that
public fun FeatureId.draggable( public fun FeatureId.draggable(
//TODO add constraints //TODO add constraints
callback: DragHandle = DragHandle.BYPASS callback: DragHandle = DragHandle.BYPASS,
) { ) {
val handle = DragHandle.withPrimaryButton { event, start, end -> val handle = DragHandle.withPrimaryButton { event, start, end ->
val feature = features[this] as? DraggableMapFeature ?: return@withPrimaryButton true val feature = features[this] as? DraggableMapFeature ?: return@withPrimaryButton true
@ -72,7 +72,7 @@ public class MapFeaturesState internal constructor(
// }.keys // }.keys
// } // }
public fun <T> forEachWithAttribute(key: Attribute<T>, block: (id: FeatureId, attributeValue: T) -> Unit) { public inline fun <T> forEachWithAttribute(key: Attribute<T>, block: (id: FeatureId, attributeValue: T) -> Unit) {
attributes.forEach { (id, attributeMap) -> attributes.forEach { (id, attributeMap) ->
attributeMap[key]?.let { attributeMap[key]?.let {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@ -126,6 +126,16 @@ public fun MapFeaturesState.circle(
id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color) id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
) )
public fun MapFeaturesState.rectangle(
centerCoordinates: Gmc,
zoomRange: IntRange = defaultZoomRange,
size: DpSize = DpSize(5.dp, 5.dp),
color: Color = Color.Red,
id: FeatureId? = null,
): FeatureId = addFeature(
id, MapRectangleFeature(centerCoordinates, zoomRange, size, color)
)
public fun MapFeaturesState.rectangle( public fun MapFeaturesState.rectangle(
centerCoordinates: Pair<Double, Double>, centerCoordinates: Pair<Double, Double>,
zoomRange: IntRange = defaultZoomRange, zoomRange: IntRange = defaultZoomRange,

View File

@ -146,11 +146,10 @@ public fun MapView(
} }
val featureDrag: DragHandle = DragHandle.withPrimaryButton { event, start, end -> val featureDrag: DragHandle = DragHandle.withPrimaryButton { event, start, end ->
var bypass = true
featureState.forEachWithAttribute(DraggableAttribute) { _, handle -> featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
bypass = bypass and handle.handle(event, start, end) if(!handle.handle(event, start, end)) return@withPrimaryButton false
} }
bypass true
} }

View File

@ -116,20 +116,17 @@ public actual fun MapView(
val dpEnd = dragChange.position.toDpOffset() val dpEnd = dragChange.position.toDpOffset()
//apply drag handle and check if it prohibits the drag even propagation //apply drag handle and check if it prohibits the drag even propagation
if ( if (selectionStart == null && !config.dragHandle.handle(
!config.dragHandle.handle(
event, event,
MapViewPoint(dpStart.toGeodetic(), viewPoint.zoom), MapViewPoint(dpStart.toGeodetic(), viewPoint.zoom),
MapViewPoint(dpEnd.toGeodetic(), viewPoint.zoom) MapViewPoint(dpEnd.toGeodetic(), viewPoint.zoom)
) )
) { ) {
//clear selection just in case
selectionStart = null
return@drag return@drag
} }
if (event.buttons.isPrimaryPressed) { if (event.buttons.isPrimaryPressed) {
//Evaluating selection frame //If selection process is started, modify the frame
selectionStart?.let { start -> selectionStart?.let { start ->
val offset = dragChange.position val offset = dragChange.position
selectRect = Rect( selectRect = Rect(
@ -141,8 +138,8 @@ public actual fun MapView(
return@drag return@drag
} }
config.onClick(MapViewPoint(dpPos.toGeodetic(), viewPoint.zoom), event) // config.onClick(MapViewPoint(dpPos.toGeodetic(), viewPoint.zoom), event)
//If no selection, drag map
setViewPoint( setViewPoint(
viewPoint.move( viewPoint.move(
-dragAmount.x.toDp().value / tileScale, -dragAmount.x.toDp().value / tileScale,