From 8c79c913e6f97788cb0b28330ca8128fc5a7b2ec Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 14 Sep 2022 12:11:15 +0300 Subject: [PATCH] Fix draggable --- .../center/sciprog/maps/compose/MapFeature.kt | 19 +++----- .../sciprog/maps/compose/MapFeaturesState.kt | 21 ++++---- .../center/sciprog/maps/compose/MapView.kt | 48 +++++++++---------- .../center/sciprog/maps/compose/MapViewJvm.kt | 34 +++++++------ 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt index b4a359b..7f22970 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt @@ -5,8 +5,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.PointMode 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.graphics.vector.VectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.IntSize @@ -136,23 +136,18 @@ public class MapBitmapImageFeature( public class MapVectorImageFeature( public val position: GeodeticMapCoordinates, - public val painter: Painter, - public val size: DpSize, + public val image: ImageVector, + public val size: DpSize = DpSize(20.dp, 20.dp), override val zoomRange: IntRange = defaultZoomRange, ) : DraggableMapFeature { override fun getBoundingBox(zoom: Double): GmcRectangle = GmcRectangle(position, position) override fun withCoordinates(newCoordinates: GeodeticMapCoordinates): MapFeature = - MapVectorImageFeature(newCoordinates, painter, size, zoomRange) -} + MapVectorImageFeature(newCoordinates, image, size, zoomRange) -@Composable -public fun MapVectorImageFeature( - position: GeodeticMapCoordinates, - image: ImageVector, - size: DpSize = DpSize(20.dp, 20.dp), - zoomRange: IntRange = defaultZoomRange, -): MapVectorImageFeature = MapVectorImageFeature(position, rememberVectorPainter(image), size, zoomRange) + @Composable + public fun painter(): VectorPainter = rememberVectorPainter(image) +} /** * A group of other features diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeaturesState.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeaturesState.kt index a4b8de2..38a26c9 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeaturesState.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeaturesState.kt @@ -38,16 +38,16 @@ public class MapFeaturesState internal constructor( return safeId } - public fun setAttribute(id: FeatureId, key: MapFeaturesState.Attribute, value: T) { + public fun setAttribute(id: FeatureId, key: Attribute, value: T) { attributes.getOrPut(id) { mutableStateMapOf() }[key] = value } @Suppress("UNCHECKED_CAST") - public fun getAttribute(id: FeatureId, key: MapFeaturesState.Attribute): T? = + public fun getAttribute(id: FeatureId, key: Attribute): T? = attributes[id]?.get(key)?.let { it as T } @Suppress("UNCHECKED_CAST") - public fun findAllWithAttribute(key: MapFeaturesState.Attribute, condition: (T) -> Boolean): Set { + public fun findAllWithAttribute(key: Attribute, condition: (T) -> Boolean): Set { return attributes.filterValues { condition(it[key] as T) }.keys @@ -56,13 +56,13 @@ public class MapFeaturesState internal constructor( @Composable public fun rememberMapFeatureState( - builder: @Composable MapFeaturesState.() -> Unit = {}, + builder: MapFeaturesState.() -> Unit = {}, ): MapFeaturesState = remember(builder) { MapFeaturesState( mutableStateMapOf(), mutableStateMapOf() - ) -}.apply { builder() } + ).apply(builder) +} public fun MapFeaturesState.circle( center: GeodeticMapCoordinates, @@ -183,7 +183,6 @@ public fun MapFeaturesState.points( id: FeatureId? = null, ): FeatureId = addFeature(id, MapPointsFeature(points.map { it.toCoordinates() }, zoomRange, stroke, color, pointMode)) -@Composable public fun MapFeaturesState.image( position: Pair, image: ImageVector, @@ -192,13 +191,15 @@ public fun MapFeaturesState.image( id: FeatureId? = null, ): FeatureId = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange)) -@Composable public fun MapFeaturesState.group( zoomRange: IntRange = defaultZoomRange, id: FeatureId? = null, - builder: @Composable MapFeaturesState.() -> Unit, + builder: MapFeaturesState.() -> Unit, ): FeatureId { - val map = rememberMapFeatureState(builder).features() + val map = MapFeaturesState( + mutableStateMapOf(), + mutableStateMapOf() + ).apply(builder).features() val feature = MapFeatureGroup(map, zoomRange) return addFeature(id, feature) } diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt index 1a4bdbf..1f579ba 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt @@ -87,7 +87,7 @@ internal fun GmcRectangle.computeViewPoint( return MapViewPoint(center, zoom) } -private val defaultCanvasSize = DpSize(512.dp, 512.dp) +internal val defaultCanvasSize = DpSize(512.dp, 512.dp) /** * Draw a map using convenient parameters. If neither [initialViewPoint], noe [initialRectangle] is defined, @@ -103,46 +103,44 @@ public fun MapView( initialRectangle: GmcRectangle? = null, config: MapViewConfig = MapViewConfig(), modifier: Modifier = Modifier.fillMaxSize(), - buildFeatures: @Composable (MapFeaturesState.() -> Unit) = {}, + buildFeatures: MapFeaturesState.() -> Unit = {}, ): Unit = key(buildFeatures) { val featureState = rememberMapFeatureState(buildFeatures) val features = featureState.features() - var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) } - - val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle, cachedCanvasSize) { + val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle) { initialViewPoint - ?: initialRectangle?.computeViewPoint(mapTileProvider, cachedCanvasSize) - ?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, cachedCanvasSize) + ?: initialRectangle?.computeViewPoint(mapTileProvider, defaultCanvasSize) + ?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, defaultCanvasSize) ?: MapViewPoint.globe } - val featureDrag by derivedStateOf { - DragHandle.withPrimaryButton { _, start, end -> - val zoom = start.zoom - featureState.findAllWithAttribute(DraggableAttribute) { it }.forEach { id -> - val feature = features[id] as? DraggableMapFeature ?: return@forEach - val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach - if (start.focus in boundingBox) { - featureState.addFeature(id, feature.withCoordinates(end.focus)) - return@withPrimaryButton false + val featureDrag by remember { + derivedStateOf { + DragHandle.withPrimaryButton { _, start, end -> + val zoom = start.zoom + featureState.findAllWithAttribute(DraggableAttribute) { it }.forEach { id -> + val feature = features[id] as? DraggableMapFeature ?: return@forEach + val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach + if (start.focus in boundingBox) { + featureState.addFeature(id, feature.withCoordinates(end.focus)) + return@withPrimaryButton false + } } + return@withPrimaryButton true } - return@withPrimaryButton true } } - val newConfig by derivedStateOf { - config.copy( - dragHandle = DragHandle.combine(featureDrag, config.dragHandle), - onCanvasSizeChange = { canvasSize -> - config.onCanvasSizeChange(canvasSize) - cachedCanvasSize = canvasSize - } - ) + val newConfig by remember { + derivedStateOf { + config.copy( + dragHandle = DragHandle.combine(featureDrag, config.dragHandle) + ) + } } diff --git a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt index ef581cd..3c4120a 100644 --- a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt +++ b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt @@ -45,7 +45,6 @@ private val logger = KotlinLogging.logger("MapView") /** * A component that renders map and provides basic map manipulation capabilities */ - @Composable public actual fun MapView( mapTileProvider: MapTileProvider, @@ -54,10 +53,12 @@ public actual fun MapView( config: MapViewConfig, modifier: Modifier, ): Unit = key(initialViewPoint) { - var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) } + var canvasSize by remember { mutableStateOf(defaultCanvasSize) } var viewPoint by remember { mutableStateOf(initialViewPoint) } + require(viewPoint.zoom in 1.0..18.0) { "Zoom value of ${viewPoint.zoom} is not valid" } + fun setViewPoint(newViewPoint: MapViewPoint) { config.onViewChange(newViewPoint) viewPoint = newViewPoint @@ -68,9 +69,7 @@ public actual fun MapView( 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(mapTileProvider) { mutableStateListOf() } @@ -103,9 +102,12 @@ public actual fun MapView( val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp()) //start selection - if (event.buttons.isPrimaryPressed && event.keyboardModifiers.isShiftPressed) { - selectRect = Rect(change.position, change.position) - } + var selectionStart: Offset? = + if (event.buttons.isPrimaryPressed && event.keyboardModifiers.isShiftPressed) { + change.position + } else { + null + } drag(change.id) { dragChange -> val dragAmount = dragChange.position - dragChange.previousPosition @@ -124,19 +126,19 @@ public actual fun MapView( ) ) { //clear selection just in case - selectRect = null + selectionStart = null return@drag } if (event.buttons.isPrimaryPressed) { //Evaluating selection frame - selectRect?.let { rect -> + selectionStart?.let { start -> 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) + min(offset.x, start.x), + min(offset.y, start.y), + max(offset.x, start.x), + max(offset.y, start.y) ) return@drag } @@ -214,6 +216,8 @@ public actual fun MapView( } } + val painterCache = features.features().values.filterIsInstance().associateWith { it.painter() } + Canvas(canvasModifier) { fun WebMercatorCoordinates.toOffset(): Offset = Offset( @@ -265,7 +269,7 @@ public actual fun MapView( val offset = feature.position.toOffset() val size = feature.size.toSize() translate(offset.x - size.width / 2, offset.y - size.height / 2) { - with(feature.painter) { + with(painterCache[feature]!!) { draw(size) } }