2023-01-06 10:40:21 +03:00
|
|
|
package center.sciprog.maps.features
|
|
|
|
|
2023-02-06 10:37:22 +03:00
|
|
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
|
|
import androidx.compose.foundation.PointerMatcher
|
2023-01-06 10:40:21 +03:00
|
|
|
import androidx.compose.ui.graphics.Color
|
2023-02-13 13:32:25 +03:00
|
|
|
import androidx.compose.ui.graphics.PathEffect
|
2023-02-06 10:37:22 +03:00
|
|
|
import androidx.compose.ui.input.pointer.PointerEvent
|
|
|
|
import androidx.compose.ui.input.pointer.PointerKeyboardModifiers
|
2023-01-06 10:40:21 +03:00
|
|
|
import center.sciprog.attributes.Attribute
|
2023-02-06 10:37:22 +03:00
|
|
|
import center.sciprog.attributes.AttributesBuilder
|
2023-01-06 10:40:21 +03:00
|
|
|
import center.sciprog.attributes.SetAttribute
|
2023-02-06 10:37:22 +03:00
|
|
|
import center.sciprog.attributes.withAttribute
|
2023-01-06 10:40:21 +03:00
|
|
|
|
|
|
|
public object ZAttribute : Attribute<Float>
|
|
|
|
|
|
|
|
public object DraggableAttribute : Attribute<DragHandle<Any>>
|
|
|
|
|
|
|
|
public object DragListenerAttribute : SetAttribute<DragListener<Any>>
|
|
|
|
|
2023-02-06 10:37:22 +03:00
|
|
|
/**
|
|
|
|
* Click radius for point-like and line objects
|
|
|
|
*/
|
|
|
|
public object ClickRadius : Attribute<Float>
|
|
|
|
|
2023-01-06 10:40:21 +03:00
|
|
|
public object ClickListenerAttribute : SetAttribute<MouseListener<Any>>
|
|
|
|
|
|
|
|
public object HoverListenerAttribute : SetAttribute<MouseListener<Any>>
|
|
|
|
|
2023-01-06 12:36:02 +03:00
|
|
|
//public object TapListenerAttribute : SetAttribute<TapListener<Any>>
|
|
|
|
|
2023-01-06 10:40:21 +03:00
|
|
|
public object VisibleAttribute : Attribute<Boolean>
|
|
|
|
|
|
|
|
public object ColorAttribute : Attribute<Color>
|
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
|
|
|
|
public fun <T : Any, F : Feature<T>> FeatureRef<T, F>.color(color: Color): FeatureRef<T, F> =
|
|
|
|
modifyAttribute(ColorAttribute, color)
|
|
|
|
|
|
|
|
|
2023-01-06 10:40:21 +03:00
|
|
|
public object ZoomRangeAttribute : Attribute<FloatRange>
|
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
|
|
|
|
public fun <T : Any, F : Feature<T>> FeatureRef<T, F>.zoomRange(range: FloatRange): FeatureRef<T, F> =
|
|
|
|
modifyAttribute(ZoomRangeAttribute, range)
|
|
|
|
|
|
|
|
|
2023-02-06 10:37:22 +03:00
|
|
|
public object AlphaAttribute : Attribute<Float>
|
|
|
|
|
|
|
|
public fun <T : Any, F : Feature<T>> FeatureRef<T, F>.modifyAttributes(modify: AttributesBuilder.() -> Unit): FeatureRef<T, F> {
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
parent.feature(
|
|
|
|
id,
|
|
|
|
resolve().withAttributes {
|
|
|
|
AttributesBuilder(this).apply(modify).build()
|
|
|
|
} as F
|
|
|
|
)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
public fun <T : Any, F : Feature<T>, V> FeatureRef<T, F>.modifyAttribute(
|
|
|
|
key: Attribute<V>,
|
|
|
|
value: V?,
|
|
|
|
): FeatureRef<T, F> {
|
2023-02-06 10:37:22 +03:00
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
parent.feature(id, resolve().withAttributes { withAttribute(key, value) } as F)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add drag to this feature
|
|
|
|
*
|
|
|
|
* @param constraint optional drag constraint
|
|
|
|
*/
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
2023-02-13 16:49:36 +03:00
|
|
|
public fun <T : Any, F : DraggableFeature<T>> FeatureRef<T, F>.draggable(
|
2023-02-06 10:37:22 +03:00
|
|
|
constraint: ((T) -> T)? = null,
|
|
|
|
listener: (PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit)? = null,
|
2023-02-13 16:49:36 +03:00
|
|
|
): FeatureRef<T, F> = with(parent) {
|
2023-02-06 10:37:22 +03:00
|
|
|
if (attributes[DraggableAttribute] == null) {
|
|
|
|
val handle = DragHandle.withPrimaryButton<Any> { event, start, end ->
|
|
|
|
val feature = featureMap[id] as? DraggableFeature<T> ?: return@withPrimaryButton DragResult(end)
|
|
|
|
start as ViewPoint<T>
|
|
|
|
end as ViewPoint<T>
|
|
|
|
if (start in feature) {
|
|
|
|
val finalPosition = constraint?.invoke(end.focus) ?: end.focus
|
|
|
|
feature(id, feature.withCoordinates(finalPosition))
|
|
|
|
feature.attributes[DragListenerAttribute]?.forEach {
|
|
|
|
it.handle(event, start, ViewPoint(finalPosition, end.zoom))
|
|
|
|
}
|
|
|
|
DragResult(ViewPoint(finalPosition, end.zoom), false)
|
|
|
|
} else {
|
|
|
|
DragResult(end, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
modifyAttribute(DraggableAttribute, handle)
|
|
|
|
}
|
|
|
|
|
|
|
|
//Apply callback
|
|
|
|
if (listener != null) {
|
|
|
|
onDrag(listener)
|
|
|
|
}
|
|
|
|
return this@draggable
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
public fun <T : Any, F : DraggableFeature<T>> FeatureRef<T, F>.onDrag(
|
|
|
|
listener: PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit,
|
|
|
|
): FeatureRef<T, F> = modifyAttributes {
|
|
|
|
DragListenerAttribute.add(
|
|
|
|
DragListener { event, from, to -> event.listener(from as ViewPoint<T>, to as ViewPoint<T>) }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
public fun <T : Any, F : DomainFeature<T>> FeatureRef<T, F>.onClick(
|
|
|
|
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
|
|
|
|
): FeatureRef<T, F> = modifyAttributes {
|
|
|
|
ClickListenerAttribute.add(
|
|
|
|
MouseListener { event, point ->
|
|
|
|
event.onClick(point as ViewPoint<T>)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
@OptIn(ExperimentalFoundationApi::class)
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
2023-02-13 16:49:36 +03:00
|
|
|
public fun <T : Any, F : DomainFeature<T>> FeatureRef<T, F>.onClick(
|
2023-02-06 10:37:22 +03:00
|
|
|
pointerMatcher: PointerMatcher,
|
|
|
|
keyboardModifiers: PointerKeyboardModifiers.() -> Boolean = { true },
|
|
|
|
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
|
|
|
|
): FeatureRef<T, F> = modifyAttributes {
|
|
|
|
ClickListenerAttribute.add(
|
|
|
|
MouseListener { event, point ->
|
|
|
|
if (pointerMatcher.matches(event) && keyboardModifiers(event.keyboardModifiers)) {
|
|
|
|
event.onClick(point as ViewPoint<T>)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
2023-02-13 16:49:36 +03:00
|
|
|
public fun <T : Any, F : DomainFeature<T>> FeatureRef<T, F>.onHover(
|
2023-02-06 10:37:22 +03:00
|
|
|
onClick: PointerEvent.(move: ViewPoint<T>) -> Unit,
|
|
|
|
): FeatureRef<T, F> = modifyAttributes {
|
|
|
|
HoverListenerAttribute.add(
|
|
|
|
MouseListener { event, point -> event.onClick(point as ViewPoint<T>) }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Suppress("UNCHECKED_CAST")
|
|
|
|
// @OptIn(ExperimentalFoundationApi::class)
|
|
|
|
// public fun <F : DomainFeature<T>> FeatureId<F>.onTap(
|
|
|
|
// pointerMatcher: PointerMatcher = PointerMatcher.Primary,
|
|
|
|
// keyboardFilter: PointerKeyboardModifiers.() -> Boolean = { true },
|
|
|
|
// onTap: (point: ViewPoint<T>) -> Unit,
|
|
|
|
// ): FeatureId<F> = modifyAttributes {
|
|
|
|
// TapListenerAttribute.add(
|
|
|
|
// TapListener(pointerMatcher, keyboardFilter) { point -> onTap(point as ViewPoint<T>) }
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
|
2023-02-13 13:32:25 +03:00
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
public object PathEffectAttribute : Attribute<PathEffect>
|
2023-02-13 13:32:25 +03:00
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
public fun <T : Any> FeatureRef<T, LineSegmentFeature<T>>.pathEffect(effect: PathEffect): FeatureRef<T, LineSegmentFeature<T>> =
|
|
|
|
modifyAttribute(PathEffectAttribute, effect)
|
2023-02-13 13:32:25 +03:00
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
public object StrokeAttribute : Attribute<Float>
|
2023-02-13 13:32:25 +03:00
|
|
|
|
2023-02-13 16:49:36 +03:00
|
|
|
public fun <T : Any, F : LineSegmentFeature<T>> FeatureRef<T, F>.stroke(width: Float): FeatureRef<T, F> =
|
|
|
|
modifyAttribute(StrokeAttribute, width)
|