add copying and pasting nodes

This commit is contained in:
Lera Elvoé 2023-11-30 12:10:43 +03:00
parent d3bf258ba9
commit 8520f240d0
No known key found for this signature in database
4 changed files with 161 additions and 44 deletions

View file

@ -59,6 +59,7 @@ func add_node_inst(node: DeckNode, assign_id: String = "", assign_to_self: bool
node._id = uuid node._id = uuid
else: else:
nodes[assign_id] = node nodes[assign_id] = node
node._id = assign_id
node_added.emit(node) node_added.emit(node)
@ -197,6 +198,86 @@ func get_group(uuid: String) -> Deck:
return groups.get(uuid) return groups.get(uuid)
func copy_nodes_json(nodes_to_copy: Array[String]) -> String:
var d := {"nodes": {}}
for node_id: String in nodes_to_copy:
d.nodes[node_id] = get_node(node_id).to_dict()
for node: String in d.nodes:
var outgoing_connections: Dictionary = d.nodes[node].outgoing_connections.duplicate(true)
for from_port: int in outgoing_connections:
for to_node: String in outgoing_connections[from_port]:
if !(to_node in nodes_to_copy):
(d.nodes[node].outgoing_connections[from_port] as Dictionary).erase(to_node)
var incoming_connections: Dictionary = d.nodes[node].incoming_connections.duplicate(true)
for to_port: int in incoming_connections:
for from_node: String in incoming_connections[to_port]:
if !(from_node in nodes_to_copy):
(d.nodes[node].incoming_connections[to_port] as Dictionary).erase(from_node)
for node: Dictionary in d.nodes.values().slice(1):
node.position.x = node.position.x - d.nodes.values()[0].position.x
node.position.y = node.position.y - d.nodes.values()[0].position.y
d.nodes.values()[0].position.x = 0
d.nodes.values()[0].position.y = 0
return JSON.stringify(d)
func allocate_ids(count: int) -> Array[String]:
var res: Array[String]
for i in count:
res.append(UUID.v4())
return res
func paste_nodes_from_json(json: String, position: Vector2 = Vector2()) -> void:
var d: Dictionary = JSON.parse_string(json) as Dictionary
if !d.get("nodes"):
return
var new_ids := allocate_ids(d.nodes.size())
var ids_map := {}
for i: int in d.nodes.keys().size():
var node_id: String = d.nodes.keys()[i]
ids_map[node_id] = new_ids[i]
var new_json := json
for old_id: String in ids_map:
new_json = new_json.replace(old_id, ids_map[old_id])
for node_id: String in d.nodes:
d.nodes[node_id]._id = ids_map[node_id]
d.nodes[node_id].position.x += position.x
d.nodes[node_id].position.y += position.y
var outgoing_connections: Dictionary = d.nodes[node_id].outgoing_connections as Dictionary
var outgoing_connections_res := {}
for from_port: String in outgoing_connections:
outgoing_connections_res[from_port] = {}
for to_node_id: String in outgoing_connections[from_port]:
outgoing_connections_res[from_port][ids_map[to_node_id]] = outgoing_connections[from_port][to_node_id]
var incoming_connections: Dictionary = d.nodes[node_id].incoming_connections as Dictionary
var incoming_connections_res := {}
for to_port: String in incoming_connections:
incoming_connections_res[to_port] = {}
for from_node_id: String in incoming_connections[to_port]:
incoming_connections_res[to_port][ids_map[from_node_id]] = incoming_connections[to_port][from_node_id]
d.nodes[node_id].outgoing_connections = outgoing_connections_res
d.nodes[node_id].incoming_connections = incoming_connections_res
var node := DeckNode.from_dict(d.nodes[node_id])
add_node_inst(node, ids_map[node_id])
## Returns a [Dictionary] representation of this deck. ## Returns a [Dictionary] representation of this deck.
func to_dict(with_meta: bool = true) -> Dictionary: func to_dict(with_meta: bool = true) -> Dictionary:
var inner := { var inner := {
@ -239,44 +320,8 @@ static func from_dict(data: Dictionary, path: String = "") -> Deck:
var nodes_data: Dictionary = data.deck.nodes as Dictionary var nodes_data: Dictionary = data.deck.nodes as Dictionary
for node_id in nodes_data: for node_id in nodes_data:
var node := deck.add_node_type(nodes_data[node_id].node_type, node_id, false) var node := DeckNode.from_dict(nodes_data[node_id])
node._id = node_id deck.add_node_inst(node, node_id)
node.name = nodes_data[node_id].name
node._belonging_to = deck
node.position = nodes_data[node_id].position
for prop in nodes_data[node_id].props:
node.set(prop, nodes_data[node_id].props[prop])
node._pre_connection()
for from_port: String in nodes_data[node_id].outgoing_connections:
var connection_data: Dictionary = nodes_data[node_id].outgoing_connections[from_port]
node.outgoing_connections[int(from_port)] = {}
for to_node: String in connection_data:
var input_ports: Array = connection_data[to_node]
node.outgoing_connections[int(from_port)][to_node] = []
for to_input_port in input_ports:
node.outgoing_connections[int(from_port)][to_node].append(int(to_input_port))
for to_port: String in nodes_data[node_id].incoming_connections:
var connection_data = nodes_data[node_id].incoming_connections[to_port]
for connection in connection_data:
connection_data[connection] = int(connection_data[connection])
node.incoming_connections[int(to_port)] = connection_data
for i in node.ports.size():
var port_value: Variant
if (nodes_data[node_id].port_values as Array).size() <= i:
port_value = null
else:
port_value = (nodes_data[node_id].port_values as Array)[i]
node.ports[i].value = port_value
for key in nodes_data[node_id].meta:
node.set_meta(key, str_to_var(nodes_data[node_id].meta[key]))
node._post_load()
var groups_data: Dictionary = data.deck.groups as Dictionary var groups_data: Dictionary = data.deck.groups as Dictionary

View file

@ -250,12 +250,12 @@ func to_dict(with_meta: bool = true) -> Dictionary:
var d := { var d := {
"_id": _id, "_id": _id,
"name": name, "name": name,
"outgoing_connections": outgoing_connections, "outgoing_connections": outgoing_connections.duplicate(true),
"incoming_connections": incoming_connections, "incoming_connections": incoming_connections.duplicate(true),
"props": {}, "props": {},
"node_type": node_type, "node_type": node_type,
"port_values": [], "port_values": [],
"position": position, "position": position.duplicate(),
} }
for prop in props_to_serialize: for prop in props_to_serialize:
@ -273,6 +273,48 @@ func to_dict(with_meta: bool = true) -> Dictionary:
return d return d
static func from_dict(data: Dictionary) -> DeckNode:
var node := NodeDB.instance_node(data.node_type)
#node._id = data._id
node.name = data.name
node.position = data.position
for prop in data.props:
node.set(prop, data.props[prop])
node._pre_connection()
for from_port: String in data.outgoing_connections:
var connection_data: Dictionary = data.outgoing_connections[from_port]
node.outgoing_connections[int(from_port)] = {}
for to_node: String in connection_data:
var input_ports: Array = connection_data[to_node]
node.outgoing_connections[int(from_port)][to_node] = []
for to_input_port in input_ports:
node.outgoing_connections[int(from_port)][to_node].append(int(to_input_port))
for to_port: String in data.incoming_connections:
var connection_data = data.incoming_connections[to_port]
for connection in connection_data:
connection_data[connection] = int(connection_data[connection])
node.incoming_connections[int(to_port)] = connection_data
for i in node.ports.size():
var port_value: Variant
if (data.port_values as Array).size() <= i:
port_value = null
else:
port_value = (data.port_values as Array)[i]
node.ports[i].value = port_value
for key in data.meta:
node.set_meta(key, str_to_var(data.meta[key]))
node._post_load()
return node
## Returns the node's [member position] as a [Vector2]. ## Returns the node's [member position] as a [Vector2].
func position_as_vector2() -> Vector2: func position_as_vector2() -> Vector2:
return Vector2(position.x, position.y) return Vector2(position.x, position.y)

View file

@ -129,10 +129,13 @@ func refresh_connections() -> void:
for from_port in node.outgoing_connections: for from_port in node.outgoing_connections:
for to_node_id: String in node.outgoing_connections[from_port]: for to_node_id: String in node.outgoing_connections[from_port]:
var to_node_ports = node.outgoing_connections[from_port][to_node_id] var to_node_ports = node.outgoing_connections[from_port][to_node_id]
var to_node: DeckNodeRendererGraphNode = get_children().filter( var renderer: Array = get_children().filter(
func(c: DeckNodeRendererGraphNode): func(c: DeckNodeRendererGraphNode):
return c.node._id == to_node_id return c.node._id == to_node_id
)[0] )
if renderer.is_empty():
break
var to_node: DeckNodeRendererGraphNode = renderer[0]
for to_node_port: int in to_node_ports: for to_node_port: int in to_node_ports:
connect_node( connect_node(
from_node.name, from_node.name,
@ -148,6 +151,8 @@ func _on_deck_node_added(node: DeckNode) -> void:
inst.node = node inst.node = node
add_child(inst) add_child(inst)
inst.position_offset = inst.node.position_as_vector2() inst.position_offset = inst.node.position_as_vector2()
clear_connections()
refresh_connections()
## Connected to [signal Deck.node_added], used to remove the specified ## Connected to [signal Deck.node_added], used to remove the specified
## [DeckNodeRendererGraphNode] and queue_free it. ## [DeckNodeRendererGraphNode] and queue_free it.
@ -229,5 +234,28 @@ func _on_delete_nodes_request(nodes: Array[StringName]) -> void:
for node_id in node_ids: for node_id in node_ids:
deck.remove_node(node_id, true) deck.remove_node(node_id, true)
#await get_tree().process_frame
refresh_connections() refresh_connections()
func _on_copy_nodes_request() -> void:
var selected := get_selected_nodes()
if selected.is_empty():
return
var selected_ids: Array[String]
selected_ids.assign(selected.map(
func(x: DeckNodeRendererGraphNode):
return x.node._id
))
DisplayServer.clipboard_set(deck.copy_nodes_json(selected_ids))
func _on_paste_nodes_request() -> void:
var clip := DisplayServer.clipboard_get()
var node_pos := (get_local_mouse_position() + scroll_offset - (Vector2(75.0, 0.0) * zoom)) / zoom
if snapping_enabled:
node_pos = node_pos.snapped(Vector2(snapping_distance, snapping_distance))
deck.paste_nodes_from_json(clip, node_pos)

View file

@ -12,6 +12,8 @@ right_disconnects = true
show_arrange_button = false show_arrange_button = false
script = ExtResource("1_pojfs") script = ExtResource("1_pojfs")
[connection signal="copy_nodes_request" from="." to="." method="_on_copy_nodes_request"]
[connection signal="delete_nodes_request" from="." to="." method="_on_delete_nodes_request"] [connection signal="delete_nodes_request" from="." to="." method="_on_delete_nodes_request"]
[connection signal="paste_nodes_request" from="." to="." method="_on_paste_nodes_request"]
[connection signal="popup_request" from="." to="." method="_on_popup_request"] [connection signal="popup_request" from="." to="." method="_on_popup_request"]
[connection signal="scroll_offset_changed" from="." to="." method="_on_scroll_offset_changed"] [connection signal="scroll_offset_changed" from="." to="." method="_on_scroll_offset_changed"]