More robust drag API and implementation

This commit is contained in:
Alexander Nozik 2022-11-28 07:56:15 +03:00
parent e7cb006a3c
commit 1ebcb02f0a
4 changed files with 48 additions and 27 deletions

View File

@ -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

View File

@ -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,10 +23,25 @@ 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,17 +57,31 @@ 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") // @Suppress("UNCHECKED_CAST")
public fun <T> findAllWithAttribute(key: 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
// }
public fun <T> forEachWithAttribute(key: Attribute<T>, block: (id: FeatureId, attributeValue: T) -> Unit) {
attributes.forEach { (id, attributeMap) ->
attributeMap[key]?.let {
@Suppress("UNCHECKED_CAST")
block(id, it as T)
}
}
} }
public companion object{
public companion object {
/** /**
* Build, but do not remember map feature state * Build, but do not remember map feature state

View File

@ -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
}
} }
return@withPrimaryButton true bypass
} }

View File

@ -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 (