Hello all :wave: I'm very new to Compose. I need t...
# compose-desktop
k
Hello all 👋 I'm very new to Compose. I need to create a desktop application which mostly shows data on tile maps. I didn't find any existing controls for it and I'd like to create one. I have however hard time wrapping my head around how do I do that in Compose. Basically I need a rectangle pane, in which I show grid of images. The pane must react to dragging with mouse (move map around) and scrolling (zoom in and out). On top of the pane I want to draw markers - circles and polylines. It is easy for me to think of implementation if I just have a 2D canvas on which I can draw as in Swing. What do I need to read about in Compose to better understand how to implement it? I think good starting points into the topic are: • How do I detect dragging? • How do I draw custom shapes on top of grid of images?
I got a DM with a few pointers. I think the most important for me was that Jetpack Compose documentation in Android docs is most of the time applicable to Compose Desktop. When it comes to dragging, there seems to be support for it here: https://developer.android.com/jetpack/compose/gestures#dragging As for drawing, there is Canvas API documented here: https://developer.android.com/jetpack/compose/graphics I am not yet sure whether it is possible to handle mouse scroll wheel, as it is not mentioned here: https://github.com/JetBrains/compose-jb/tree/master/tutorials/Mouse_Events I may also need to think a bit about handling state: map's center, zoom. Not yet sure what is the right way for it in Compose
👌 1
s
I am not yet sure whether it is possible to handle mouse scroll wheel, as it is not mentioned here
I haven’t tried this at all, but compose for desktop is basically just a Java Swing application drawing on a canvas (Maybe that’s an oversimplification?) My main point is, you have access to all the same stuff that a normal Java GUI application has access too. i.e. Mouse scroll wheel support. I’d look at something like this if you just need to detect the event https://docs.oracle.com/javase/tutorial/uiswing/events/mousewheellistener.html
m
The Canvas has a Modifier for that.
Copy code
.mouseScrollFilter(onMouseScroll = { ev, _ ->
    when (ev.delta) {
        is MouseScrollUnit.Line -> {
            val deltaValue = (ev.delta as MouseScrollUnit.Line).value
            val zoomOut = deltaValue < 0.0
            pointerLocation?.let { setScaleBy(if (zoomOut) 1.2 else 1/1.2, it) }
        }
    }
    true})
I use that to zoom in and out to my canvas.
k
Perfect, thank you!
v
Here is Modifier for Box containing my scalable map view:
Copy code
Modifier
    .size(maxWidth, maxHeight)
    .clip(RoundedCornerShape(8.dp))
    .mouseScrollFilter { event, _ -> // make a map zoom
        val scrollUnit = event.delta
        val multiplier = if (scrollUnit is MouseScrollUnit.Line) {
            if (scrollUnit.value > 0f) 1 / scrollStep else scrollStep
        } else 1.0
        currentArea = currentArea.zoom(multiplier, currentPoint)
        true
    }
    .pointerMoveFilter( // change position of mouse pointer
        onMove = { offset ->
            currentPoint = MapPoint(offset)
            previousDragPoint = null
            true
        }
    )
    .pointerInput(Unit) { // make a map zoom by double clicking
        detectTapGestures(
            onDoubleTap = { offset ->
                currentArea = currentArea.zoom(sqrt(2.0), currentPoint)
            },
        )
    }
    .pointerInput(Unit) { // make a map shift
        detectDragGestures(
            onDrag = { change, _ ->
                currentArea = currentArea.shift(
                    change.position - (previousDragPoint ?: change.previousPosition)
                )
                previousDragPoint = change.position
                currentPoint = MapPoint(change.position)
            }
        )
    }
currentPoint and currentArea - mutable states containing coordinates of the mouse pointer and displayed area of the map in georgraphical coordinates
When currentArea changing, running LaunchedEffect:
Copy code
LaunchedEffect(currentArea) {
    if (currentArea.isValid && currentArea != currentImage?.area) {
        if (currentImage != null) delay(100) // pause to accumulate changes
        progress = true
        currentImage = MapRenderer().render(currentArea) // render map image for currentArea
        progress = false
    }
}
MapRenderer creates an image of the current area from tiles of the appropriate scale
currentPoint for displaying hints above the current coordinates and for zooming the map around this point
k
Thank you for sharing Vadim!
👀 1