Working references with drag and drop
Jeff Lindsay Nov 7
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.
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 data to a data attribute. Then it triggers a different event in Go where it can read the data off the data attribute.
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.