mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
c4e35043df
no longer using classes for every type. the type system has been greatly simplified, with the added bonus that it hooks directly into GraphEdit's slot type system. connections will still fail if the type conversion fails, which may be used by other renderers. the type conversion map is straightforward to understand, and easy to extend should the need arise (hopefully it shouldn't). Reviewed-on: https://codeberg.org/Eroax/Re-DotDeck/pulls/8 Co-authored-by: Lera Elvoé <yagich@poto.cafe> Co-committed-by: Lera Elvoé <yagich@poto.cafe>
225 lines
8.6 KiB
GDScript
225 lines
8.6 KiB
GDScript
extends GraphEdit
|
|
class_name DeckRendererGraphEdit
|
|
|
|
## Reference to the [DeckNodeRendererGraphNode] used for later instantiation
|
|
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
|
|
|
|
## 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]
|
|
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)
|
|
|
|
for t: DeckType.Types in DeckType.CONVERSION_MAP:
|
|
for out_type: DeckType.Types in DeckType.CONVERSION_MAP[t]:
|
|
add_valid_connection_type(t, out_type)
|
|
|
|
add_node_menu.node_selected.connect(_on_add_node_menu_node_selected)
|
|
|
|
connection_request.connect(attempt_connection)
|
|
disconnection_request.connect(attempt_disconnect)
|
|
|
|
## Receives [signal GraphEdit.connection_request] and attempts to create a
|
|
## connection between the two [DeckNode]s involved, utilizes [NodeDB] for accessing them.
|
|
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))
|
|
|
|
#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)
|
|
|
|
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
|
|
)
|
|
|
|
## Receives [signal GraphEdit.disconnection_request] and attempts to disconnect the two [DeckNode]s
|
|
## involved, utilizes [NodeDB] for accessing them.
|
|
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))
|
|
|
|
#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)
|
|
|
|
deck.disconnect_nodes(from_node_renderer.node, to_node_renderer.node, from_port, to_port)
|
|
|
|
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():
|
|
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)
|
|
|
|
## Setups all the data from the set [member deck] in this [DeckRendererGraphEdit]
|
|
func initialize_from_deck() -> void:
|
|
for i in get_children():
|
|
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().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().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().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():
|
|
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().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()
|