More robust drag API and implementation
This commit is contained in:
parent
e7cb006a3c
commit
1ebcb02f0a
@ -1,8 +1,8 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
|
||||||
compose.version=1.2.0
|
compose.version=1.2.1
|
||||||
|
|
||||||
agp.version=4.2.2
|
agp.version=4.2.2
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
toolsVersion=0.13.1-kotlin-1.7.20
|
toolsVersion=0.13.3-kotlin-1.7.20
|
@ -14,7 +14,7 @@ import center.sciprog.maps.coordinates.*
|
|||||||
|
|
||||||
public typealias FeatureId = String
|
public typealias FeatureId = String
|
||||||
|
|
||||||
public object DraggableAttribute : MapFeaturesState.Attribute<Boolean>
|
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>,
|
||||||
@ -23,9 +23,24 @@ public class MapFeaturesState internal constructor(
|
|||||||
public interface Attribute<T>
|
public interface Attribute<T>
|
||||||
|
|
||||||
//TODO use context receiver for that
|
//TODO use context receiver for that
|
||||||
public fun FeatureId.draggable(enabled: Boolean = true) {
|
public fun FeatureId.draggable(
|
||||||
setAttribute(this, DraggableAttribute, enabled)
|
//TODO add constraints
|
||||||
|
callback: DragHandle = DragHandle.BYPASS
|
||||||
|
) {
|
||||||
|
val handle = DragHandle.withPrimaryButton { event, start, end ->
|
||||||
|
val feature = features[this] as? DraggableMapFeature ?: return@withPrimaryButton true
|
||||||
|
val boundingBox = feature.getBoundingBox(start.zoom) ?: return@withPrimaryButton true
|
||||||
|
if (start.focus in boundingBox) {
|
||||||
|
addFeature(this, feature.withCoordinates(end.focus))
|
||||||
|
callback.handle(event, start, end)
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
setAttribute(this, DraggableAttribute, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun features(): Map<FeatureId, MapFeature> = features
|
public fun features(): Map<FeatureId, MapFeature> = features
|
||||||
|
|
||||||
@ -42,16 +57,30 @@ public class MapFeaturesState internal constructor(
|
|||||||
attributes.getOrPut(id) { mutableStateMapOf() }[key] = value
|
attributes.getOrPut(id) { mutableStateMapOf() }[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun removeAttribute(id: FeatureId, key: Attribute<*>) {
|
||||||
|
attributes[id]?.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun <T> getAttribute(id: FeatureId, key: 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")
|
||||||
|
// public fun <T> findAllWithAttribute(key: Attribute<T>, condition: (T) -> Boolean): Set<FeatureId> {
|
||||||
|
// return attributes.filterValues {
|
||||||
|
// condition(it[key] as T)
|
||||||
|
// }.keys
|
||||||
|
// }
|
||||||
|
|
||||||
|
public fun <T> forEachWithAttribute(key: Attribute<T>, block: (id: FeatureId, attributeValue: T) -> Unit) {
|
||||||
|
attributes.forEach { (id, attributeMap) ->
|
||||||
|
attributeMap[key]?.let {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun <T> findAllWithAttribute(key: Attribute<T>, condition: (T) -> Boolean): Set<FeatureId> {
|
block(id, it as T)
|
||||||
return attributes.filterValues {
|
|
||||||
condition(it[key] as T)
|
|
||||||
}.keys
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +37,7 @@ public fun interface DragHandle {
|
|||||||
if (event.buttons.isPrimaryPressed) {
|
if (event.buttons.isPrimaryPressed) {
|
||||||
block(event, start, end)
|
block(event, start, end)
|
||||||
} else {
|
} else {
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,17 +145,12 @@ public fun MapView(
|
|||||||
?: MapViewPoint.globe
|
?: MapViewPoint.globe
|
||||||
}
|
}
|
||||||
|
|
||||||
val featureDrag = DragHandle.withPrimaryButton { _, start, end ->
|
val featureDrag: DragHandle = DragHandle.withPrimaryButton { event, start, end ->
|
||||||
val zoom = start.zoom
|
var bypass = true
|
||||||
featureState.findAllWithAttribute(DraggableAttribute) { it }.forEach { id ->
|
featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
|
||||||
val feature = features[id] as? DraggableMapFeature ?: return@forEach
|
bypass = bypass and handle.handle(event, start, end)
|
||||||
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
|
|
||||||
if (start.focus in boundingBox) {
|
|
||||||
featureState.addFeature(id, feature.withCoordinates(end.focus))
|
|
||||||
return@withPrimaryButton false
|
|
||||||
}
|
}
|
||||||
}
|
bypass
|
||||||
return@withPrimaryButton true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,11 +112,8 @@ public actual fun MapView(
|
|||||||
|
|
||||||
drag(change.id) { dragChange ->
|
drag(change.id) { dragChange ->
|
||||||
val dragAmount = dragChange.position - dragChange.previousPosition
|
val dragAmount = dragChange.position - dragChange.previousPosition
|
||||||
val dpStart = DpOffset(
|
val dpStart = dragChange.previousPosition.toDpOffset()
|
||||||
dragChange.previousPosition.x.toDp(),
|
val dpEnd = dragChange.position.toDpOffset()
|
||||||
dragChange.previousPosition.y.toDp()
|
|
||||||
)
|
|
||||||
val dpEnd = DpOffset(dragChange.position.x.toDp(), dragChange.position.y.toDp())
|
|
||||||
|
|
||||||
//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 (
|
||||||
|
Loading…
Reference in New Issue
Block a user