Cloning dynamically created GUI nodes with no ids – using next()

Hi,
I use GUI nodes for a puzzle game, where a “block” can have a “selected” state, visualized by another child node with a texture.
All block nodes are created dynamically without assigning an individual id to them. I store only the reference to each node in an array.

Now I want to clone a block node with gui.clone_tree( block_node ).
Since I don’t have any ids to access the resulting table of this function, I use next() for getting the first node (the root I assume).

The block is then cloned a the same position but without showing the “selected” state texture.

When I then assign a position to the root node reference, nothing changes visually, even though internally the correct position is stored, if I print out gui.get_position():

local new_pos = vmath.vector3(100, 100, 0)   
local node_tree = gui.clone_tree(node)
local _, root_block_node = next(node_tree)
gui.set_position(root_block_node, new_pos)

print(gui.get_position(root_block_node)) -- correct position

Things become even less comprehensible (to me) when I test assigning a parent node to the root block like so:

local blocks_node = gui.get_node("blocks") -- the parent of all created blocks
gui.set_parent(root_block_node, blocks_node, true)

Then the child node (the node for state “selected”) will be set to the position I want instead. But the cloned block node remains in its position!

The examples and code in the forum I have found of clone_tree() always involve the use of the id string. But it would be great if you could solve it without.

How can this behavior be explained and how can I solve the problem? Thanks!

local node_tree = gui.clone_tree(node)
local cloned_node_tree = node_tree[hash("root_node")] -- you need this one
gui.set_position(cloned_node_tree, new_pos)

node_tree[hash(“root_node”)] returns nil in my case.
The problem is, my nodes are created dynamically with no ids.

Try printing out the results of gui.clone_tree with pprint() to see what it contains. That should make it more clear.

I don’t think you’re getting the right node. Rama’s code should be correct, only you should replace “root_node” with the actual name of your root node (the one you cloned).

next() will not give you anything that could be considered the “first” value, and the “first” value won’t necessarily be the root node, since they are keyed by the original node names (hashed). If you look at the Lua manual entry for next(), you will see:

The order in which the indices are enumerated is not specified, even for numeric indices.

In other words: next() gives you an essentially random key and value. It’s main use is just to check if a table is empty or not.

1 Like

Thanks for the clarification!
I have printed out the cloned tree before, even with a loop, but don’t saw any usable keys.

If I print out the cloned node tree with pprint() I get for example:

{ --[[0x1214c0640]]
  hash: [] = box@(0, 0, 0)
}

So I read this as: There is no id / hash and the new node is on position 0, 0, 0

1 Like

Aha, interesting. Are you cloning a node that you created dynamically? (such as with gui.new_box_node()?) They get an “empty” id, which is a hashed empty string: hash(""). That’s what you’re seeing as the key.

But anyway, since there is only one node and no child nodes, that should eliminate the possibility that you are getting the wrong node, leaving the problem still a mystery… :thinking:

Is the project small? Could you zip it up (without the “.internal” or “build” folders) and post it here?

This rings a bell, I think you have to manually set the id for dynamically created nodes:

Related issue:

2 Likes

Ohhh, good point, that really throws a wrench in the works if you clone a dynamically created tree of nodes. All the IDs used for keys are the same, so the table returned from clone_tree has only one node in it. :cold_sweat:

Just did a quick test. If you don’t set the IDs before cloning, you just get a table with one node in it, with no way to tell which one it is.

local _pos = vmath.vector3()
local _size = vmath.vector3(100, 100, 0)

local function box(w, h, x, y)
	_pos.x, _pos.y = x or 0, y or 0
	_size.x, _size.y = w or 100, h or 100
	return gui.new_box_node(_pos, _size)
end

function init(self)
	local parent = box()
	local child = box()
	gui.set_parent(child, parent)
	-- gui.set_id(parent, "parent") -- Without these, 'nodes' has only one node in it.
	-- gui.set_id(child, "child")
	local nodes = gui.clone_tree(parent)
	pprint(nodes)
end
1 Like

Oh, that’s odd, but I think I will build a simple workaround by creating the game object as a new node.
This is not so hard in my case, since there is only one more child node.

PS: Here in the docs I had found the following quote:

Dynamically created nodes do not have an id assigned to them. This is by design. The references that are returned from gui.new_[type]_node(), gui.clone() and gui.clone_tree() are the only thing necessary to be able to access the nodes and you should keep track of that reference.
GUI scripts in Defold

It sounded to me like I actually didn’t need to manually assign ids.

1 Like