miggor-StreamGraph/graph_node_renderer/deck_renderer_graph_edit.gd

224 lines
8.7 KiB
GDScript3
Raw Normal View History

2023-06-12 17:32:36 +02:00
extends GraphEdit
class_name DeckRendererGraphEdit
2023-06-12 17:32:36 +02:00
## Reference to the [DeckNodeRendererGraphNode] used for later instantiation
2023-06-12 17:32:36 +02:00
const NODE_SCENE := preload("res://graph_node_renderer/deck_node_renderer_graph_node.tscn")
## Reference to the [AddNodeMenu] used for later instantiation
const ADD_NODE_MENU_SCENE := preload("res://graph_node_renderer/add_node_menu.tscn")
## The [PopupPanel] that holds the [AddNodeMenu] scene.
var search_popup_panel: PopupPanel
## Stores instance of [AddNodeMenu] that is used under [member search_popup_panel]
var add_node_menu: AddNodeMenu
## Used to specify the size of [member search_popup_panel].
@export var search_popup_size: Vector2i = Vector2i(500, 300)
## Stores the position of the [member search_popup_panel] for use when adding
## nodes in [method _on_add_node_menu_node_selected]
var popup_position: Vector2
2023-06-12 17:32:36 +02:00
## References the [Deck] that holds all the functional properties of this [DeckRendererGraphEdit]
var deck: Deck:
set(v):
deck = v
deck.node_added.connect(_on_deck_node_added)
deck.node_removed.connect(_on_deck_node_removed)
## Emits when Group creation is requested. Ex. Hitting the "group_nodes" Hotkey.
signal group_enter_requested(group_id: String)
## Sets up the [member search_popup_panel] with an instance of [member ADD_NODE_SCENE]
## stored in [member add_node_menu]. And sets its size of [member search_popup_panel] to
## [member add_node_popup_size]
2023-06-12 17:32:36 +02:00
func _ready() -> void:
add_node_menu = ADD_NODE_MENU_SCENE.instantiate()
search_popup_panel = PopupPanel.new()
search_popup_panel.add_child(add_node_menu)
search_popup_panel.size = search_popup_size
add_child(search_popup_panel, false, Node.INTERNAL_MODE_BACK)
2023-06-12 17:32:36 +02:00
add_node_menu.node_selected.connect(_on_add_node_menu_node_selected)
2023-06-12 17:59:30 +02:00
connection_request.connect(attempt_connection)
2023-06-13 15:06:17 +02:00
disconnection_request.connect(attempt_disconnect)
2023-06-12 17:59:30 +02:00
## Receives [signal GraphEdit.connection_request] and attempts to create a
## connection between the two [DeckNode]s involved, utilizes [NodeDB] for accessing them.
2023-06-12 17:59:30 +02:00
func attempt_connection(from_node_name: StringName, from_port: int, to_node_name: StringName, to_port: int) -> void:
var from_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(from_node_name))
var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node_name))
2023-06-12 17:59:30 +02:00
#var from_output := from_node_renderer.node.get_global_port_idx_from_output(from_port)
#var to_input := to_node_renderer.node.get_global_port_idx_from_input(to_port)
2023-06-12 17:59:30 +02:00
if deck.connect_nodes(from_node_renderer.node, to_node_renderer.node, from_port, to_port):
connect_node(
from_node_renderer.name,
from_port,
to_node_renderer.name,
to_port
)
2023-06-13 15:06:17 +02:00
## Receives [signal GraphEdit.disconnection_request] and attempts to disconnect the two [DeckNode]s
## involved, utilizes [NodeDB] for accessing them.
2023-06-13 15:06:17 +02:00
func attempt_disconnect(from_node_name: StringName, from_port: int, to_node_name: StringName, to_port: int) -> void:
var from_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(from_node_name))
var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node_name))
2023-06-13 15:06:17 +02:00
#var from_output := from_node_renderer.node.get_global_port_idx_from_output(from_port)
#var to_input := to_node_renderer.node.get_global_port_idx_from_input(to_port)
2023-06-13 15:06:17 +02:00
deck.disconnect_nodes(from_node_renderer.node, to_node_renderer.node, from_port, to_port)
2023-06-13 15:06:17 +02:00
disconnect_node(
from_node_renderer.name,
from_port,
to_node_renderer.name,
to_port
)
## Returns the associated [DeckNodeRendererGraphNode] for the supplied [DeckNode].
## Or [code]null[/code] if none is found.
func get_node_renderer(node: DeckNode) -> DeckNodeRendererGraphNode:
for i: DeckNodeRendererGraphNode in get_children().slice(1):
if i.node == node:
return i
return null
## Updates [member Deck]s meta property "offset" whenever [member GraphEdit.scroll_offset]
func _on_scroll_offset_changed(offset: Vector2) -> void:
deck.set_meta("offset", offset)
2023-07-21 10:10:24 +02:00
## Setups all the data from the set [member deck] in this [DeckRendererGraphEdit]
func initialize_from_deck() -> void:
# TODO: wait for https://github.com/godotengine/godot/issues/85005 to get merged
# until it is, all calls to GraphEdit#get_children will need to slice off the first element
for i in get_children().slice(1):
i.queue_free()
scroll_offset = deck.get_meta("offset", Vector2())
for node_id in deck.nodes:
var node_renderer: DeckNodeRendererGraphNode = NODE_SCENE.instantiate()
node_renderer.node = deck.nodes[node_id]
add_child(node_renderer)
node_renderer.position_offset = node_renderer.node.position_as_vector2()
for node_id in deck.nodes:
var node: DeckNode = deck.nodes[node_id]
var from_node = get_children().slice(1).filter(
func(c: DeckNodeRendererGraphNode):
return c.node._id == node_id
)[0]
refresh_connections()
## Loops through all [DeckNode]s in [member Deck.nodes] and calls
## [method GraphEdit.connect_node] for all the connections that exist in each
func refresh_connections() -> void:
for node_id in deck.nodes:
var node: DeckNode = deck.nodes[node_id]
var from_node: DeckNodeRendererGraphNode = get_children().slice(1).filter(
func(c: DeckNodeRendererGraphNode):
return c.node._id == node_id
)[0]
for from_port in node.outgoing_connections:
for connection in node.outgoing_connections[from_port]:
var to_node_id = connection.keys()[0]
var to_node_port = connection.values()[0]
var to_node: DeckNodeRendererGraphNode = get_children().slice(1).filter(
func(c: DeckNodeRendererGraphNode):
return c.node._id == to_node_id
)[0]
#print("***")
#print("calling connect_node with:")
#print(from_node.node.name)
#print(from_node.node.get_port_type_idx_from_global(from_port))
#print(to_node.node.name)
#print(to_node.node.get_port_type_idx_from_global(to_node_port))
#print("***")
connect_node(
from_node.name,
from_port,
to_node.name,
to_node_port
)
## Connected to [signal Deck.node_added], used to instance the required
## [DeckNodeRendererGraphNode] and set it's [member DeckNodeRenderGraphNode.position_offset]
func _on_deck_node_added(node: DeckNode) -> void:
var inst: DeckNodeRendererGraphNode = NODE_SCENE.instantiate()
inst.node = node
add_child(inst)
inst.position_offset = inst.node.position_as_vector2()
## Connected to [signal Deck.node_added], used to remove the specified
## [DeckNodeRendererGraphNode] and queue_free it.
func _on_deck_node_removed(node: DeckNode) -> void:
for renderer: DeckNodeRendererGraphNode in get_children().slice(1):
if renderer.node != node:
continue
renderer.queue_free()
break
## Utility function that gets all [DeckNodeRenderGraphNode]s that are selected
## See [member GraphNode.selected]
func get_selected_nodes() -> Array:
return get_children().slice(1).filter(
func(x: DeckNodeRendererGraphNode):
return x.selected
)
## Executes functionality based off hotkey inputs. Specifically handles creating groups
## based off the action "group_nodes".
func _gui_input(event: InputEvent) -> void:
if event.is_action_pressed("group_nodes") && get_selected_nodes().size() > 0:
print("?")
clear_connections()
var nodes = get_selected_nodes().map(
func(x: DeckNodeRendererGraphNode):
return x.node
)
deck.group_nodes(nodes)
refresh_connections()
get_viewport().set_input_as_handled()
## Handles entering groups with action "enter_group". Done here to bypass neighbor
## functionality.
func _input(event: InputEvent) -> void:
if !has_focus():
return
if event.is_action_pressed("enter_group") && get_selected_nodes().size() == 1:
if !((get_selected_nodes()[0] as DeckNodeRendererGraphNode).node.node_type == "group_node"):
return
print("tried to enter group")
group_enter_requested.emit((get_selected_nodes()[0] as DeckNodeRendererGraphNode).node.group_id)
get_viewport().set_input_as_handled()
## Opens [member search_popup_panel] at the mouse position. Connected to [signal GraphEdit.popup_request]
func _on_popup_request(p_popup_position: Vector2) -> void:
var p := get_viewport_rect().position + get_global_mouse_position()
p += Vector2(10, 10)
var r := Rect2i(p, search_popup_panel.size)
search_popup_panel.popup_on_parent(r)
add_node_menu.focus_search_bar()
popup_position = p_popup_position
## Connected to [signal AddNodeMenu.node_selected] and creates a [DeckNode] using
## [method NodeDB.instance_node]. Then placing it at the [member scroll_offset] +
## [member popup_position] / [member zoom]
func _on_add_node_menu_node_selected(type: String) -> void:
var node := NodeDB.instance_node(type) as DeckNode
deck.add_node_inst(node)
var node_pos := ((scroll_offset + popup_position) / zoom)
if snapping_enabled:
node_pos = node_pos.snapped(Vector2(snapping_distance, snapping_distance))
get_node_renderer(node).position_offset = node_pos
search_popup_panel.hide()