Refactored drag processing

This commit is contained in:
Alexander Nozik 2022-08-30 10:29:56 +03:00
parent 26aaac2ecd
commit 5a1d3d701f
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
3 changed files with 100 additions and 62 deletions

View File

@ -7,6 +7,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PointMode
import androidx.compose.ui.input.pointer.isPrimaryPressed
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import center.sciprog.maps.compose.*
@ -58,14 +59,18 @@ fun App() {
config = MapViewConfig(
inferViewBoxFromFeatures = true,
onViewChange = { centerCoordinates = focus },
onDrag = { start, end ->
if (start.focus.latitude.degrees.value in (pointTwo.first - 0.05)..(pointTwo.first + 0.05) &&
dragHandle = { event, start, end ->
if (!event.buttons.isPrimaryPressed) {
true
} else if (start.focus.latitude.degrees.value in (pointTwo.first - 0.05)..(pointTwo.first + 0.05) &&
start.focus.longitude.degrees.value in (pointTwo.second - 0.05)..(pointTwo.second + 0.05)
) {
pointTwo = pointTwo.first + (end.focus.latitude - start.focus.latitude).degrees.value to
pointTwo.second + (end.focus.longitude - start.focus.longitude).degrees.value
false// returning false, because when we are dragging circle we don't want to drag map
} else true
} else {
true
}
}
)
) {

View File

@ -3,6 +3,7 @@ package center.sciprog.maps.compose
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.unit.DpSize
import center.sciprog.maps.coordinates.*
import kotlin.math.PI
@ -10,16 +11,29 @@ import kotlin.math.log2
import kotlin.math.min
public fun interface DragHandle {
/**
* @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 end - end point of the drag
*
* @return true if default event processors should be used after this one
*/
public fun drag(event: PointerEvent, start: MapViewPoint, end: MapViewPoint): Boolean
public companion object {
public val BYPASS: DragHandle = DragHandle { _, _, _ -> true }
}
}
//TODO consider replacing by modifier
/**
* @param onDrag - returns true if you want to drag a map and false, if you want to make map stationary.
* start - is a point where drag begins, end is a point where drag ends
*/
public data class MapViewConfig(
val zoomSpeed: Double = 1.0 / 3.0,
val inferViewBoxFromFeatures: Boolean = false,
val onClick: MapViewPoint.() -> Unit = {},
val onDrag: (start: MapViewPoint, end: MapViewPoint) -> Boolean = { _, _ -> true },
val onClick: MapViewPoint.(PointerEvent) -> Unit = {},
val dragHandle: DragHandle = DragHandle.BYPASS,
val onViewChange: MapViewPoint.() -> Unit = {},
val onSelect: (GmcRectangle) -> Unit = {},
val zoomOnSelect: Boolean = true,

View File

@ -80,9 +80,14 @@ public actual fun MapView(
}
}
val zoom: Int by derivedStateOf { floor(viewPoint.zoom).toInt() }
val zoom: Int by derivedStateOf {
require(viewPoint.zoom in 1.0..18.0) { "Zoom value of ${viewPoint.zoom} is not valid" }
floor(viewPoint.zoom).toInt()
}
val tileScale: Double by derivedStateOf { 2.0.pow(viewPoint.zoom - zoom) }
val tileScale: Double by derivedStateOf {
2.0.pow(viewPoint.zoom - zoom)
}
val mapTiles = remember { mutableStateListOf<MapTile>() }
@ -109,61 +114,75 @@ public actual fun MapView(
fun Offset.toDpOffset() = DpOffset(x.toDp(), y.toDp())
val event: PointerEvent = awaitPointerEvent()
event.changes.forEach { change ->
if (event.buttons.isPrimaryPressed) {
//Evaluating selection frame
if (event.keyboardModifiers.isShiftPressed) {
selectRect = Rect(change.position, change.position)
drag(change.id) { dragChange ->
selectRect?.let { rect ->
val offset = dragChange.position
selectRect = Rect(
min(offset.x, rect.left),
min(offset.y, rect.top),
max(offset.x, rect.right),
max(offset.y, rect.bottom)
)
}
}
selectRect?.let { rect ->
//Use selection override if it is defined
val gmcBox = GmcRectangle(
rect.topLeft.toDpOffset().toGeodetic(),
rect.bottomRight.toDpOffset().toGeodetic()
)
config.onSelect(gmcBox)
if (config.zoomOnSelect) {
val newViewPoint = gmcBox.computeViewPoint(mapTileProvider).invoke(canvasSize)
config.onViewChange(newViewPoint)
viewPointInternal = newViewPoint
}
selectRect = null
}
} else {
val dragStart = change.position
val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp())
config.onClick(MapViewPoint(dpPos.toGeodetic(), viewPoint.zoom))
drag(change.id) { dragChange ->
val dragAmount = dragChange.position - dragChange.previousPosition
val dpStart = DpOffset(
dragChange.previousPosition.x.toDp(),
dragChange.previousPosition.y.toDp()
)
val dpEnd = DpOffset(dragChange.position.x.toDp(), dragChange.position.y.toDp())
if (!config.onDrag(
MapViewPoint(dpStart.toGeodetic(), viewPoint.zoom),
MapViewPoint(dpEnd.toGeodetic(), viewPoint.zoom)
)
) return@drag
val newViewPoint = viewPoint.move(
-dragAmount.x.toDp().value / tileScale,
+dragAmount.y.toDp().value / tileScale
)
config.onViewChange(newViewPoint)
viewPointInternal = newViewPoint
}
event.changes.forEach { change ->
val dragStart = change.position
val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp())
//start selection
if (event.buttons.isPrimaryPressed && event.keyboardModifiers.isShiftPressed) {
selectRect = Rect(change.position, change.position)
}
drag(change.id) { dragChange ->
val dragAmount = dragChange.position - dragChange.previousPosition
val dpStart = DpOffset(
dragChange.previousPosition.x.toDp(),
dragChange.previousPosition.y.toDp()
)
val dpEnd = DpOffset(dragChange.position.x.toDp(), dragChange.position.y.toDp())
if (
!config.dragHandle.drag(
event,
MapViewPoint(dpStart.toGeodetic(), viewPoint.zoom),
MapViewPoint(dpEnd.toGeodetic(), viewPoint.zoom)
)
) {
//clear selection just in case
selectRect = null
return@drag
}
if (event.buttons.isPrimaryPressed) {
//Evaluating selection frame
selectRect?.let { rect ->
val offset = dragChange.position
selectRect = Rect(
min(offset.x, rect.left),
min(offset.y, rect.top),
max(offset.x, rect.right),
max(offset.y, rect.bottom)
)
return@drag
}
config.onClick(MapViewPoint(dpPos.toGeodetic(), viewPoint.zoom), event)
val newViewPoint = viewPoint.move(
-dragAmount.x.toDp().value / tileScale,
+dragAmount.y.toDp().value / tileScale
)
config.onViewChange(newViewPoint)
viewPointInternal = newViewPoint
}
}
// evaluate selection
selectRect?.let { rect ->
//Use selection override if it is defined
val gmcBox = GmcRectangle(
rect.topLeft.toDpOffset().toGeodetic(),
rect.bottomRight.toDpOffset().toGeodetic()
)
config.onSelect(gmcBox)
if (config.zoomOnSelect) {
val newViewPoint = gmcBox.computeViewPoint(mapTileProvider).invoke(canvasSize)
config.onViewChange(newViewPoint)
viewPointInternal = newViewPoint
}
selectRect = null
}
}
}