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.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PointMode import androidx.compose.ui.graphics.PointMode
import androidx.compose.ui.input.pointer.isPrimaryPressed
import androidx.compose.ui.window.Window import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application import androidx.compose.ui.window.application
import center.sciprog.maps.compose.* import center.sciprog.maps.compose.*
@ -58,14 +59,18 @@ fun App() {
config = MapViewConfig( config = MapViewConfig(
inferViewBoxFromFeatures = true, inferViewBoxFromFeatures = true,
onViewChange = { centerCoordinates = focus }, onViewChange = { centerCoordinates = focus },
onDrag = { start, end -> dragHandle = { event, start, end ->
if (start.focus.latitude.degrees.value in (pointTwo.first - 0.05)..(pointTwo.first + 0.05) && 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) 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 = pointTwo.first + (end.focus.latitude - start.focus.latitude).degrees.value to
pointTwo.second + (end.focus.longitude - start.focus.longitude).degrees.value 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 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.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.DpSize
import center.sciprog.maps.coordinates.* import center.sciprog.maps.coordinates.*
import kotlin.math.PI import kotlin.math.PI
@ -10,16 +11,29 @@ import kotlin.math.log2
import kotlin.math.min 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 //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( public data class MapViewConfig(
val zoomSpeed: Double = 1.0 / 3.0, val zoomSpeed: Double = 1.0 / 3.0,
val inferViewBoxFromFeatures: Boolean = false, val inferViewBoxFromFeatures: Boolean = false,
val onClick: MapViewPoint.() -> Unit = {}, val onClick: MapViewPoint.(PointerEvent) -> Unit = {},
val onDrag: (start: MapViewPoint, end: MapViewPoint) -> Boolean = { _, _ -> true }, val dragHandle: DragHandle = DragHandle.BYPASS,
val onViewChange: MapViewPoint.() -> Unit = {}, val onViewChange: MapViewPoint.() -> Unit = {},
val onSelect: (GmcRectangle) -> Unit = {}, val onSelect: (GmcRectangle) -> Unit = {},
val zoomOnSelect: Boolean = true, 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>() } val mapTiles = remember { mutableStateListOf<MapTile>() }
@ -109,61 +114,75 @@ public actual fun MapView(
fun Offset.toDpOffset() = DpOffset(x.toDp(), y.toDp()) fun Offset.toDpOffset() = DpOffset(x.toDp(), y.toDp())
val event: PointerEvent = awaitPointerEvent() 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) event.changes.forEach { change ->
viewPointInternal = newViewPoint val dragStart = change.position
} val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp())
selectRect = null
} //start selection
} else { if (event.buttons.isPrimaryPressed && event.keyboardModifiers.isShiftPressed) {
val dragStart = change.position selectRect = Rect(change.position, change.position)
val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp()) }
config.onClick(MapViewPoint(dpPos.toGeodetic(), viewPoint.zoom))
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 = DpOffset(
dragChange.previousPosition.x.toDp(), dragChange.previousPosition.x.toDp(),
dragChange.previousPosition.y.toDp() dragChange.previousPosition.y.toDp()
) )
val dpEnd = DpOffset(dragChange.position.x.toDp(), dragChange.position.y.toDp()) val dpEnd = DpOffset(dragChange.position.x.toDp(), dragChange.position.y.toDp())
if (!config.onDrag(
MapViewPoint(dpStart.toGeodetic(), viewPoint.zoom), if (
MapViewPoint(dpEnd.toGeodetic(), viewPoint.zoom) !config.dragHandle.drag(
) event,
) return@drag MapViewPoint(dpStart.toGeodetic(), viewPoint.zoom),
val newViewPoint = viewPoint.move( MapViewPoint(dpEnd.toGeodetic(), viewPoint.zoom)
-dragAmount.x.toDp().value / tileScale, )
+dragAmount.y.toDp().value / tileScale ) {
) //clear selection just in case
config.onViewChange(newViewPoint) selectRect = null
viewPointInternal = newViewPoint 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
} }
} }
} }