Detect taps manually
This commit is contained in:
parent
f288a17243
commit
5a3a4b059e
@ -1,4 +1,6 @@
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.PointerMatcher
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
@ -34,6 +36,7 @@ private fun GeodeticMapCoordinates.toShortString(): String =
|
||||
"${(latitude.degrees.value).toString().take(6)}:${(longitude.degrees.value).toString().take(6)}"
|
||||
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@Preview
|
||||
fun App() {
|
||||
@ -120,9 +123,9 @@ fun App() {
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
|
||||
//Add click listeners for all polygons
|
||||
forEachWithType<Gmc, PolygonFeature<Gmc>> { id, feature ->
|
||||
id.onClick {
|
||||
id.onClick(PointerMatcher.Primary) {
|
||||
println("Click on $id")
|
||||
//draw in top-level scope
|
||||
with(this@MapView) {
|
||||
|
@ -37,6 +37,7 @@ public abstract class CoordinateViewScope<T : Any>(
|
||||
|
||||
public abstract fun DpOffset.toCoordinates(): T
|
||||
|
||||
|
||||
public abstract fun T.toDpOffset(): DpOffset
|
||||
|
||||
public fun T.toOffset(density: Density): Offset = with(density) {
|
||||
@ -44,6 +45,11 @@ public abstract class CoordinateViewScope<T : Any>(
|
||||
Offset(dpOffset.x.toPx(), dpOffset.y.toPx())
|
||||
}
|
||||
|
||||
public fun Offset.toCoordinates(density: Density): T = with(density) {
|
||||
val dpOffset = DpOffset(x.toDp(), y.toDp())
|
||||
dpOffset.toCoordinates()
|
||||
}
|
||||
|
||||
public abstract fun Rectangle<T>.toDpRect(): DpRect
|
||||
|
||||
public abstract fun ViewPoint<T>.moveBy(x: Dp, y: Dp): ViewPoint<T>
|
||||
|
@ -1,5 +1,7 @@
|
||||
package center.sciprog.maps.features
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.PointerMatcher
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -10,6 +12,7 @@ import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.PointerEvent
|
||||
import androidx.compose.ui.input.pointer.PointerKeyboardModifiers
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -144,13 +147,30 @@ public data class FeatureGroup<T : Any>(
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <F : DomainFeature<T>> FeatureId<F>.onClick(
|
||||
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
|
||||
): FeatureId<F> = modifyAttributes {
|
||||
ClickListenerAttribute.add(
|
||||
MouseListener { event, point -> event.onClick(point as ViewPoint<T>) }
|
||||
MouseListener { event, point ->
|
||||
event.onClick(point as ViewPoint<T>)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <F : DomainFeature<T>> FeatureId<F>.onClick(
|
||||
pointerMatcher: PointerMatcher,
|
||||
keyboardModifiers: PointerKeyboardModifiers.() -> Boolean = {true},
|
||||
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
|
||||
): FeatureId<F> = modifyAttributes {
|
||||
ClickListenerAttribute.add(
|
||||
MouseListener { event, point ->
|
||||
if (pointerMatcher.matches(event) && keyboardModifiers(event.keyboardModifiers)) {
|
||||
event.onClick(point as ViewPoint<T>)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -163,6 +183,18 @@ public data class FeatureGroup<T : Any>(
|
||||
)
|
||||
}
|
||||
|
||||
// @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>) }
|
||||
// )
|
||||
// }
|
||||
|
||||
public fun <F : Feature<T>> FeatureId<F>.color(color: Color): FeatureId<F> =
|
||||
modifyAttribute(ColorAttribute, color)
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
package center.sciprog.maps.features
|
||||
|
||||
import androidx.compose.ui.input.pointer.PointerEvent
|
||||
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
||||
|
||||
public fun interface MouseListener<in T : Any> {
|
||||
public fun handle(event: PointerEvent, point: ViewPoint<T>): Unit
|
||||
|
||||
public companion object {
|
||||
public fun <T : Any> withPrimaryButton(
|
||||
block: (event: PointerEvent, click: ViewPoint<T>) -> Unit,
|
||||
): MouseListener<T> = MouseListener { event, click ->
|
||||
if (event.buttons.isPrimaryPressed) {
|
||||
block(event, click)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@OptIn(ExperimentalFoundationApi::class)
|
||||
//public class TapListener<in T : Any>(
|
||||
// public val pointerMatcher: PointerMatcher,
|
||||
// public val keyboardFilter: PointerKeyboardModifiers.() -> Boolean = { true },
|
||||
// public val onTap: (point: ViewPoint<T>) -> Unit,
|
||||
//)
|
@ -1,23 +1,7 @@
|
||||
package center.sciprog.maps.features
|
||||
|
||||
import androidx.compose.ui.input.pointer.PointerEvent
|
||||
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
|
||||
public fun interface MouseListener<in T : Any> {
|
||||
public fun handle(event: PointerEvent, point: ViewPoint<T>): Unit
|
||||
|
||||
public companion object {
|
||||
public fun <T : Any> withPrimaryButton(
|
||||
block: (event: PointerEvent, click: ViewPoint<T>) -> Unit,
|
||||
): MouseListener<T> = MouseListener { event, click ->
|
||||
if (event.buttons.isPrimaryPressed) {
|
||||
block(event, click)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public data class ViewConfig<T : Any>(
|
||||
val zoomSpeed: Float = 1f / 3f,
|
||||
val onClick: MouseListener<T>? = null,
|
||||
|
@ -14,6 +14,8 @@ public object ClickListenerAttribute : SetAttribute<MouseListener<Any>>
|
||||
|
||||
public object HoverListenerAttribute : SetAttribute<MouseListener<Any>>
|
||||
|
||||
//public object TapListenerAttribute : SetAttribute<TapListener<Any>>
|
||||
|
||||
public object VisibleAttribute : Attribute<Boolean>
|
||||
|
||||
public object ColorAttribute : Attribute<Color>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package center.sciprog.maps.compose
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.gestures.drag
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
@ -16,16 +17,24 @@ import kotlin.math.min
|
||||
* Create a modifier for Map/Scheme canvas controls on desktop
|
||||
* @param features a collection of features to be rendered in descending [ZAttribute] order
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
public fun <T : Any> Modifier.mapControls(
|
||||
state: CoordinateViewScope<T>,
|
||||
features: FeatureGroup<T>,
|
||||
): Modifier = with(state) {
|
||||
|
||||
// //selecting all tapabales ahead of time
|
||||
// val allTapable = buildMap {
|
||||
// features.forEachWithAttribute(TapListenerAttribute) { _, feature, listeners ->
|
||||
// put(feature, listeners)
|
||||
// }
|
||||
// }
|
||||
|
||||
pointerInput(Unit) {
|
||||
fun Offset.toDpOffset() = DpOffset(x.toDp(), y.toDp())
|
||||
awaitPointerEventScope {
|
||||
while (true) {
|
||||
val event = awaitPointerEvent()
|
||||
val coordinates = event.changes.first().position.toDpOffset().toCoordinates()
|
||||
val coordinates = event.changes.first().position.toCoordinates(this)
|
||||
val point = space.ViewPoint(coordinates, zoom)
|
||||
|
||||
if (event.type == PointerEventType.Move) {
|
||||
@ -36,22 +45,31 @@ public fun <T : Any> Modifier.mapControls(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.type == PointerEventType.Release) {
|
||||
|
||||
if (event.type == PointerEventType.Press) {
|
||||
withTimeoutOrNull(500) {
|
||||
while (true) {
|
||||
if (awaitPointerEvent().type == PointerEventType.Release) {
|
||||
config.onClick?.handle(
|
||||
event,
|
||||
point
|
||||
)
|
||||
features.forEachWithAttribute(ClickListenerAttribute) { _, feature, listeners ->
|
||||
features.forEachWithAttributeUntil(ClickListenerAttribute) { _, feature, listeners ->
|
||||
if (point in feature as DomainFeature) {
|
||||
listeners.forEach { it.handle(event, point) }
|
||||
return@forEachWithAttribute
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.pointerInput(Unit) {
|
||||
fun Offset.toDpOffset() = DpOffset(x.toDp(), y.toDp())
|
||||
awaitPointerEventScope {
|
||||
while (true) {
|
||||
val event: PointerEvent = awaitPointerEvent()
|
||||
@ -84,11 +102,11 @@ public fun <T : Any> Modifier.mapControls(
|
||||
//apply drag handle and check if it prohibits the drag even propagation
|
||||
if (selectionStart == null) {
|
||||
val dragStart = space.ViewPoint(
|
||||
dragChange.previousPosition.toDpOffset().toCoordinates(),
|
||||
dragChange.previousPosition.toCoordinates(this),
|
||||
zoom
|
||||
)
|
||||
val dragEnd = space.ViewPoint(
|
||||
dragChange.position.toDpOffset().toCoordinates(),
|
||||
dragChange.position.toCoordinates(this),
|
||||
zoom
|
||||
)
|
||||
val dragResult = config.dragHandle?.handle(event, dragStart, dragEnd)
|
||||
@ -146,3 +164,18 @@ public fun <T : Any> Modifier.mapControls(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.pointerInput(Unit) {
|
||||
allTapable.forEach { (feature, listeners) ->
|
||||
listeners.forEach { listener ->
|
||||
detectTapGestures(listener.pointerMatcher, listener.keyboardFilter) { offset ->
|
||||
val point = space.ViewPoint(offset.toCoordinates(this@pointerInput), zoom)
|
||||
if (point in feature as DomainFeature) {
|
||||
listener.onTap(point)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
Loading…
Reference in New Issue
Block a user