loading...

Working references with drag and drop

progrium profile image Jeff Lindsay ・3 min read

Fun step forward today even if it isn't much to demo. Added support for pointers to other nodes in the tree from any component. The goal is to continue borrowing this Node and Component data model from Unity. You have Nodes in a tree, and Nodes have Components on them. In my case a Component can be any Go struct, ideally without anything special about it. Fields with pointers should be allowed to be references to a Component of another Node.

unity ui

As with Unity, making this reference in the Inspector would just involve dragging a Node to the field and it would find the appropriate Component and set the pointer to it. To start, I'm just trying to get pointers to other Nodes and then I can make it lookup specific Components later.

I started with drag and drop. HTML5 has a drag and drop API, which is nice to finally have (I know we've had it for a bit). First I added a separate drag and drop demo just to get used to the API. Then I had to figure out how to integrate with the jsTree drag and drop plugin. It turns out to not use the HTML5 drag and drop, but it has an option to integrate with it. I got jsTree nodes to be draggable to my demo drop area and pass the node ID.

From an ID, given the root Node I could find the particular Node in that tree. However, I did not have a reference to the root Node from where I needed. And since the root Node lives in the TreeView component, which is a sibling to the Inspector element, references their parent has to either were not available to bind. Instead I passed a lookup callback down and it turned out to give a reference to the root Node.

From here it was pretty easy. When a drop event happens I grab the Node ID, use the root Node to get a reference to the actual Node, and set the field to it. When the input renders, it looks up the Node and grabs its friendly name for the input value. This becomes more complex when you can reference Components instead of Nodes, but it would be the same interaction and behavior.

One weird problem I ran into was that I couldn't get data out of dataTransfer on the drag event from Go. This is how the Node ID is passed with the drag. I figured maybe something is doing a shallow copy or dataTransfer is just magical for some reason. I'll have to investigate more, but I was able just assign some handlers written in JavaScript that write the dataTransfer data to a data attribute. Then it triggers a different event in Go where it can read the data off the data attribute.

demo

So it works right?! Well, yes. But. A new problem. When we serialize the data, Go's JSON marshaler follows pointers and serializes their value. So any reference to another part of the tree is going to make a copy of the subtree. And setting the field to the Node its on will cause a call stack exceeded panic.

I have a plan, though. Since we already have to wrap Components to serialize their type, we can walk the Component value to find references to Nodes in the tree and store their ID in the Component wrapper. Then on unmarshal we look them up and set them in the right place using reflection. But that's a task for another time.

Posted on by:

progrium profile

Jeff Lindsay

@progrium

Independent open source innovator. Invented webhooks, co-created Docker, started Dokku, built RequestBin, inspired Ngrok, envisioned serverless, and instigated the modern hackathon. Also I make games.

Discussion

markdown guide