Cleanup drag
This commit is contained in:
parent
42dbbeea58
commit
572adf041f
@ -76,22 +76,19 @@ fun App() {
|
|||||||
line(drag3, drag1, id = "connection3", color = Color.Magenta)
|
line(drag3, drag1, id = "connection3", color = Color.Magenta)
|
||||||
}
|
}
|
||||||
|
|
||||||
rectangle(drag1, size = DpSize(10.dp, 10.dp)).draggable { _, _, end ->
|
rectangle(drag1, size = DpSize(10.dp, 10.dp)).draggable { _, end ->
|
||||||
drag1 = end.focus
|
drag1 = end
|
||||||
updateLine()
|
updateLine()
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rectangle(drag2, size = DpSize(10.dp, 10.dp)).draggable { _, _, end ->
|
rectangle(drag2, size = DpSize(10.dp, 10.dp)).draggable { _, end ->
|
||||||
drag2 = end.focus
|
drag2 = end
|
||||||
updateLine()
|
updateLine()
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rectangle(drag3, size = DpSize(10.dp, 10.dp)).draggable { _, _, end ->
|
rectangle(drag3, size = DpSize(10.dp, 10.dp)).draggable { _, end ->
|
||||||
drag3 = end.focus
|
drag3 = end
|
||||||
updateLine()
|
updateLine()
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -87,17 +87,24 @@ public fun MapView(
|
|||||||
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle) {
|
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle) {
|
||||||
initialViewPoint
|
initialViewPoint
|
||||||
?: initialRectangle?.computeViewPoint(mapTileProvider)
|
?: initialRectangle?.computeViewPoint(mapTileProvider)
|
||||||
?: featureState.features.values.computeBoundingBox(GmcCoordinateSpace,1f)?.computeViewPoint(mapTileProvider)
|
?: featureState.features.values.computeBoundingBox(GmcCoordinateSpace, 1f)
|
||||||
|
?.computeViewPoint(mapTileProvider)
|
||||||
?: MapViewPoint.globe
|
?: MapViewPoint.globe
|
||||||
}
|
}
|
||||||
|
|
||||||
val featureDrag: DragHandle<Gmc> = DragHandle.withPrimaryButton { event, start: ViewPoint<Gmc>, end: ViewPoint<Gmc> ->
|
val featureDrag: DragHandle<Gmc> = DragHandle.withPrimaryButton { event, start, end ->
|
||||||
featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
|
featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
|
||||||
//TODO add safety
|
@Suppress("UNCHECKED_CAST")
|
||||||
handle as DragHandle<Gmc>
|
(handle as DragHandle<Gmc>)
|
||||||
if (!handle.handle(event, start, end)) return@withPrimaryButton false
|
.handle(event, start, end)
|
||||||
|
.takeIf { !it.handleNext }
|
||||||
|
?.let {
|
||||||
|
//we expect it already have no bypass
|
||||||
|
return@withPrimaryButton it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
//bypass
|
||||||
|
DragResult(end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ public actual fun MapView(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
featuresState.features.values.filter { viewPoint.zoom in it.zoomRange }.sortedBy { it.depth }.forEach { feature ->
|
featuresState.features.values.filter { viewPoint.zoom in it.zoomRange }.sortedBy { it.z }.forEach { feature ->
|
||||||
drawFeature(state, painterCache, feature)
|
drawFeature(state, painterCache, feature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ package center.sciprog.maps.features
|
|||||||
import androidx.compose.runtime.mutableStateMapOf
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
public object DepthAttribute : Feature.Attribute<Float>
|
public object ZAttribute : Feature.Attribute<Float>
|
||||||
|
|
||||||
public object DraggableAttribute : Feature.Attribute<DragHandle<*>>
|
public object DraggableAttribute : Feature.Attribute<DragHandle<Any>>
|
||||||
|
|
||||||
public object SelectableAttribute : Feature.Attribute<(FeatureId<*>, SelectableFeature<*>) -> Unit>
|
public object SelectableAttribute : Feature.Attribute<(FeatureId<*>, SelectableFeature<*>) -> Unit>
|
||||||
|
|
||||||
@ -46,8 +46,8 @@ public class AttributeMap {
|
|||||||
override fun toString(): String = "AttributeMap(value=${map.entries})"
|
override fun toString(): String = "AttributeMap(value=${map.entries})"
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Feature<*>.depth: Float
|
public var Feature<*>.z: Float
|
||||||
get() = attributes[DepthAttribute] ?: 0f
|
get() = attributes[ZAttribute] ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
attributes[DepthAttribute] = value
|
attributes[ZAttribute] = value
|
||||||
}
|
}
|
@ -3,7 +3,13 @@ package center.sciprog.maps.features
|
|||||||
import androidx.compose.ui.input.pointer.PointerEvent
|
import androidx.compose.ui.input.pointer.PointerEvent
|
||||||
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
||||||
|
|
||||||
public fun interface DragHandle<T: Any>{
|
/**
|
||||||
|
* @param result - the endpoint of the drag to perform constrained drag
|
||||||
|
* @param handleNext - if false do not evaluate subsequent drag handles
|
||||||
|
*/
|
||||||
|
public data class DragResult<T : Any>(val result: ViewPoint<T>, val handleNext: Boolean = true)
|
||||||
|
|
||||||
|
public fun interface DragHandle<T : Any> {
|
||||||
/**
|
/**
|
||||||
* @param event - qualifiers of the event used for drag
|
* @param event - qualifiers of the event used for drag
|
||||||
* @param start - is a point where drag begins, end is a point where drag ends
|
* @param start - is a point where drag begins, end is a point where drag ends
|
||||||
@ -11,32 +17,36 @@ public fun interface DragHandle<T: Any>{
|
|||||||
*
|
*
|
||||||
* @return true if default event processors should be used after this one
|
* @return true if default event processors should be used after this one
|
||||||
*/
|
*/
|
||||||
public fun handle(event: PointerEvent, start: ViewPoint<T>, end: ViewPoint<T>): Boolean
|
public fun handle(event: PointerEvent, start: ViewPoint<T>, end: ViewPoint<T>): DragResult<T>
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public fun <T: Any> bypass(): DragHandle<T> = DragHandle<T> { _, _, _ -> true }
|
public fun <T : Any> bypass(): DragHandle<T> = DragHandle<T> { _, _, end -> DragResult(end) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process only events with primary button pressed
|
* Process only events with primary button pressed
|
||||||
*/
|
*/
|
||||||
public fun <T: Any> withPrimaryButton(
|
public fun <T : Any> withPrimaryButton(
|
||||||
block: (event: PointerEvent, start: ViewPoint<T>, end: ViewPoint<T>) -> Boolean,
|
block: (event: PointerEvent, start: ViewPoint<T>, end: ViewPoint<T>) -> DragResult<T>,
|
||||||
): DragHandle<T> = DragHandle { event, start, end ->
|
): DragHandle<T> = DragHandle { event, start, end ->
|
||||||
if (event.buttons.isPrimaryPressed) {
|
if (event.buttons.isPrimaryPressed) {
|
||||||
block(event, start, end)
|
block(event, start, end)
|
||||||
} else {
|
} else {
|
||||||
true
|
DragResult(end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combine several handles into one
|
* Combine several handles into one
|
||||||
*/
|
*/
|
||||||
public fun <T: Any> combine(vararg handles: DragHandle<T>): DragHandle<T> = DragHandle { event, start, end ->
|
public fun <T : Any> combine(vararg handles: DragHandle<T>): DragHandle<T> = DragHandle { event, start, end ->
|
||||||
|
var current: ViewPoint<T> = end
|
||||||
handles.forEach {
|
handles.forEach {
|
||||||
if (!it.handle(event, start, end)) return@DragHandle false
|
val result = it.handle(event, start, current)
|
||||||
|
if (!result.handleNext) return@DragHandle result else {
|
||||||
|
current = result.result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return@DragHandle true
|
return@DragHandle DragResult(current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -44,23 +44,33 @@ public class FeaturesState<T : Any>(public val coordinateSpace: CoordinateSpace<
|
|||||||
|
|
||||||
|
|
||||||
public fun <F : Feature<T>, V> setAttribute(id: FeatureId<F>, key: Feature.Attribute<V>, value: V?) {
|
public fun <F : Feature<T>, V> setAttribute(id: FeatureId<F>, key: Feature.Attribute<V>, value: V?) {
|
||||||
getFeature(id).attributes.set(key, value)
|
getFeature(id).attributes[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO use context receiver for that
|
/**
|
||||||
|
* Add drag to this feature
|
||||||
|
*
|
||||||
|
* @param constraint optional drag constraint
|
||||||
|
*
|
||||||
|
* TODO use context receiver for that
|
||||||
|
*/
|
||||||
public fun FeatureId<DraggableFeature<T>>.draggable(
|
public fun FeatureId<DraggableFeature<T>>.draggable(
|
||||||
//TODO add constraints
|
constraint: ((T) -> T)? = null,
|
||||||
callback: DragHandle<T> = DragHandle.bypass(),
|
callback: ((start: T, end: T) -> Unit) = { _, _ -> },
|
||||||
) {
|
) {
|
||||||
val handle = DragHandle.withPrimaryButton<T> { event, start, end ->
|
@Suppress("UNCHECKED_CAST")
|
||||||
val feature = featureMap[id] as? DraggableFeature ?: return@withPrimaryButton true
|
val handle = DragHandle.withPrimaryButton<Any> { _, start, end ->
|
||||||
val boundingBox = feature.getBoundingBox(start.zoom) ?: return@withPrimaryButton true
|
val startPosition = start.focus as T
|
||||||
if (start.focus in boundingBox) {
|
val endPosition = end.focus as T
|
||||||
feature(id, feature.withCoordinates(end.focus))
|
val feature = featureMap[id] as? DraggableFeature ?: return@withPrimaryButton DragResult(end)
|
||||||
callback.handle(event, start, end)
|
val boundingBox = feature.getBoundingBox(start.zoom) ?: return@withPrimaryButton DragResult(end)
|
||||||
false
|
if (startPosition in boundingBox) {
|
||||||
|
val finalPosition = constraint?.invoke(endPosition) ?: endPosition
|
||||||
|
feature(id, feature.withCoordinates(finalPosition))
|
||||||
|
callback(startPosition, finalPosition)
|
||||||
|
DragResult(ViewPoint(finalPosition, end.zoom), false)
|
||||||
} else {
|
} else {
|
||||||
true
|
DragResult(end, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setAttribute(this, DraggableAttribute, handle)
|
setAttribute(this, DraggableAttribute, handle)
|
||||||
@ -98,11 +108,14 @@ public class FeaturesState<T : Any>(public val coordinateSpace: CoordinateSpace<
|
|||||||
// }.keys
|
// }.keys
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process all features with a given attribute from the one with highest [z] to lowest
|
||||||
|
*/
|
||||||
public inline fun <A> forEachWithAttribute(
|
public inline fun <A> forEachWithAttribute(
|
||||||
key: Feature.Attribute<A>,
|
key: Feature.Attribute<A>,
|
||||||
block: (id: FeatureId<*>, attributeValue: A) -> Unit,
|
block: (id: FeatureId<*>, attributeValue: A) -> Unit,
|
||||||
) {
|
) {
|
||||||
featureMap.forEach { (id, feature) ->
|
featureMap.entries.sortedByDescending { it.value.z }.forEach { (id, feature) ->
|
||||||
feature.attributes[key]?.let {
|
feature.attributes[key]?.let {
|
||||||
block(FeatureId<Feature<T>>(id), it)
|
block(FeatureId<Feature<T>>(id), it)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package center.sciprog.maps.features
|
|||||||
/**
|
/**
|
||||||
* @param T type of coordinates used for the view point
|
* @param T type of coordinates used for the view point
|
||||||
*/
|
*/
|
||||||
public interface ViewPoint<T: Any> {
|
public interface ViewPoint<out T: Any> {
|
||||||
public val focus: T
|
public val focus: T
|
||||||
public val zoom: Float
|
public val zoom: Float
|
||||||
}
|
}
|
@ -44,13 +44,13 @@ public fun <T : Any> Modifier.mapControls(
|
|||||||
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 (selectionStart == null && !config.dragHandle.handle(
|
if (selectionStart == null) {
|
||||||
|
val dragResult = config.dragHandle.handle(
|
||||||
event,
|
event,
|
||||||
space.ViewPoint(dpStart.toCoordinates(), viewPoint.zoom),
|
space.ViewPoint(dpStart.toCoordinates(), viewPoint.zoom),
|
||||||
space.ViewPoint(dpEnd.toCoordinates(), viewPoint.zoom)
|
space.ViewPoint(dpEnd.toCoordinates(), viewPoint.zoom)
|
||||||
)
|
)
|
||||||
) {
|
if(!dragResult.handleNext) return@drag
|
||||||
return@drag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.buttons.isPrimaryPressed) {
|
if (event.buttons.isPrimaryPressed) {
|
||||||
|
@ -26,7 +26,7 @@ fun FeaturesState<XY>.background(
|
|||||||
return feature(
|
return feature(
|
||||||
id,
|
id,
|
||||||
ScalableImageFeature(coordinateSpace, box, painter = painter).apply {
|
ScalableImageFeature(coordinateSpace, box, painter = painter).apply {
|
||||||
depth = -100f
|
z = -100f
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public fun SchemeView(
|
|||||||
clipRect {
|
clipRect {
|
||||||
featuresState.features.values
|
featuresState.features.values
|
||||||
.filter { viewPoint.zoom in it.zoomRange }
|
.filter { viewPoint.zoom in it.zoomRange }
|
||||||
.sortedBy { it.depth }
|
.sortedBy { it.z }
|
||||||
.forEach { background ->
|
.forEach { background ->
|
||||||
drawFeature(state, painterCache, background)
|
drawFeature(state, painterCache, background)
|
||||||
}
|
}
|
||||||
@ -139,10 +139,12 @@ public fun SchemeView(
|
|||||||
DragHandle.withPrimaryButton { event, start: ViewPoint<XY>, end: ViewPoint<XY> ->
|
DragHandle.withPrimaryButton { event, start: ViewPoint<XY>, end: ViewPoint<XY> ->
|
||||||
featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
|
featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
|
||||||
//TODO add safety
|
//TODO add safety
|
||||||
handle as DragHandle<XY>
|
(handle as DragHandle<XY>)
|
||||||
if (!handle.handle(event, start, end)) return@withPrimaryButton false
|
.handle(event, start, end)
|
||||||
|
.takeIf { !it.handleNext }
|
||||||
|
?.let { return@withPrimaryButton it }
|
||||||
}
|
}
|
||||||
true
|
DragResult(end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user