I'd like to implement a graph view, similar to the...
# compose-desktop
m
I'd like to implement a graph view, similar to the one found in disassemblers (

example from cutter

). It should support dragging and (limited) zooming. There are many nodes, most of them won't fit onto screen. Does someone have any recommendations on how to achieve that? Should I just use one mega
Box
or `Layout`and
Modifier.offset
elements? Or maybe a skika canvas? Would that support efficiently offscreen content? Or maybe I should ask in #compose?
w
I made a similar layout structure but it was horizontal and a little more complex , I used draw behind modifier to draw Connections and Recursive Composable functions to draw out nodes , it wasn't a graph , it was a mind map
m
I have also found this thread but here I'm mostly asking about performance, because: • There may be like thousand nodes which may all change from time to time and I'd like not to render neither nodes or their changes while they're offscreen. A `LazyBox`might be needed here, is there such a thing? • While panning, I don't want to recompose/rerender nodes on each move of the mouse. Can I achieve something to moving the camera instead of moving every element?
w
A LazyBox , I didn't use that
m
Neither have I, mainly because probably it doesn't even exist
w
I don't know how to implement that , But I'm interested in this since It might be useful to me as well I was thinking if I could just write an if statement that would cause the node be hidden but that would be an additional call
Only show if node x is greater than 0 and less than screen width basically
This will cause a lot of nodes to reappear and cause a performance lag while panning probably
I am using a zoom modifier which causes the x and y positions of the node to change when the screen is panned
m
Yeah, to check rect of every node at every mouse move does not seem super smooth
It may be solved using some queues and alike but a framework support would be nice
w
Yup !
m
cc @olonho May I ask for a dev's POV on this thread?
o
IMO for really huge graphs the most optimal approach would be custom component written on top of canvas (think maps as similar problem/solution). Maybe @jim has more specific opinion.
w
But anything that is drawn on canvas is non interactive
j
The bigger challenge with drawing on canvas is widget reusability. You'll probably want to be able to support arbitrary composables within the boxes. I suppose you could use a giant canvas in the background to draw the arrows. There is nothing about Canvas that fundamentally disallows interactivity. It is slightly more challenging since Compose can't automatically forward the input events to the right widget, you'll have to route them yourself based on your own implementation of "focus" and/or coordinates. Having the ability to know what content is currently visible and automatically avoid recomposing off-screen content is something we are very much interested in (especially @Chuck Jazdzewski [G] IIRC) but it isn't on any concrete roadmaps yet. Until then, you'll need to do your own layout and viewport positioning, and based on your layout algorithm you can know if something needs to be redrawn/recomposed. It is unfortunately pretty manual right now. The Lazy widgets have rather bespoke implementations, rather than a generalized solution. I think if I were to do this, I would write my own custom Layout to do positioning and drawing of children, but I would still use composables instead of falling back to Canvas, just because I would want to be able to support arbitrary content within the boxes.
Yeah, to check rect of every node at every mouse move does not seem super smooth
You wouldn't need to check every node. There are algorithms that can do a smarter job. My favorite for general purpose checking is quadtree decomposition. I bet there are even simpler/faster algorithms when everything is known to be a rectangle and there is only a single viewport of interest. At first glance, I might consider keeping a sorted list of edges in each axis and a viewport contains everything with edges between the two viewport edges plus anything that encapsulates the viewport (probably needs to be tracked separately as another list). Same algorithm and data structure could apply for mouse intersection detection too. Anyway, point is that this is an easy thing to optimize as needed.
m
Or maybe a nested ComposeScene could be used for the graph which would produce a large texture of the whole graph and the navigation would work by displaying some portion of that texture 🤔