mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
51652ef277
first part of addressing #59 every `Port` now has a `usage_type` field that indicates whether it can be used for triggers (eg. sending and receiving events), value requests, or both. `Deck` has an additional method to validate if a potential connection is legal, which checks for the following in order: 1. the source and target nodes are not the same node; 2. the port usage is valid (trigger to trigger, value to value, both to any); 3. the port types are compatible 4. the connection doesn't already exist all node ports by default use the "both" usage, since that will be the most common use case (especially in cases where an input port can accept either a trigger and a value request but the output can only send one type), but it can be specified as an optional argument in `add_[input|output]_port()` usage types are represented in the renderer by different port icons: ![image](/attachments/28d3cfe9-c62c-4dd4-937d-64dbe87cb205) there is a reference implementation in the Compare Values and Twitch Chat Received nodes, since those were used as examples in #59. other nodes will be added as a separate PR later if this is merged, since behavior will vary greatly per node. Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/69 Co-authored-by: Lera Elvoé <yagich@poto.cafe> Co-committed-by: Lera Elvoé <yagich@poto.cafe>
214 lines
6.6 KiB
GDScript
214 lines
6.6 KiB
GDScript
# (c) 2023-present Eroax
|
|
# (c) 2023-present Yagich
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
extends GraphNode
|
|
class_name DeckNodeRendererGraphNode
|
|
|
|
## [GraphNode] based renderer of [DeckNode]
|
|
|
|
## Stores the data container [DeckNode] that the visuals of this [DeckNodeRendererGraphNode]
|
|
## are based off.
|
|
var node: DeckNode
|
|
|
|
const TYPE_COLORS := {
|
|
DeckType.Types.BOOL: Color("#7048e0"),
|
|
DeckType.Types.STRING: Color("#dbe048"),
|
|
DeckType.Types.NUMERIC: Color("#68e36c"),
|
|
DeckType.Types.ARRAY: Color("#e36868"),
|
|
DeckType.Types.DICTIONARY: Color("#79ede3"),
|
|
DeckType.Types.ANY: Color.WHITE,
|
|
}
|
|
|
|
const PORT_USAGE_ICONS := {
|
|
Port.UsageType.TRIGGER: preload("res://graph_node_renderer/textures/port_trigger_12.svg"),
|
|
Port.UsageType.VALUE_REQUEST: preload("res://graph_node_renderer/textures/port_data_request_12.svg"),
|
|
Port.UsageType.BOTH: preload("res://graph_node_renderer/textures/port_any_12.svg"),
|
|
}
|
|
|
|
## Setups up all the properties based off [member node]. Including looping through
|
|
## [method DeckNode.get_all_ports()] and setting up all the descriptors.
|
|
func _ready() -> void:
|
|
title = node.name
|
|
node.position_updated.connect(_on_node_position_updated)
|
|
resize_request.connect(_on_resize_request)
|
|
#node.port_added.connect(_on_node_port_added)
|
|
#node.port_removed.connect(_on_node_port_removed)
|
|
node.ports_updated.connect(_on_node_ports_updated)
|
|
for port in node.get_all_ports():
|
|
update_port(port)
|
|
position_offset_changed.connect(_on_position_offset_changed)
|
|
node.renamed.connect(_on_node_renamed)
|
|
if node.node_type == "group_node":
|
|
get_titlebar_hbox().tooltip_text = "Group %s" % node.group_id.left(8)
|
|
|
|
size = node.get_meta("size", Vector2())
|
|
|
|
|
|
## Connected to [signal GraphElement.position_offset_updated] and updates the
|
|
## [member node]s properties
|
|
func _on_position_offset_changed() -> void:
|
|
node.position.x = position_offset.x
|
|
node.position.y = position_offset.y
|
|
node.position_updated.emit(node.position)
|
|
(get_parent() as DeckRendererGraphEdit).dirty = true
|
|
|
|
|
|
## Connected to [member node]s [signal position_updated] to keep parity with the
|
|
## data position.
|
|
func _on_node_position_updated(new_position: Dictionary) -> void:
|
|
position_offset_changed.disconnect(_on_position_offset_changed)
|
|
position_offset.x = new_position.x
|
|
position_offset.y = new_position.y
|
|
position_offset_changed.connect(_on_position_offset_changed)
|
|
(get_parent() as DeckRendererGraphEdit).dirty = true
|
|
|
|
|
|
func _on_resize_request(new_minsize: Vector2) -> void:
|
|
node.set_meta(&"size", new_minsize)
|
|
(get_parent() as DeckRendererGraphEdit).dirty = true
|
|
|
|
|
|
## Connected to [member node]s [signal port_added] handles setting up the specified
|
|
## [member Port.descriptor] with it's required nodes/signals etc. + adding the port
|
|
## using [method GraphNode.set_slot]
|
|
func _on_node_port_added(port_idx: int) -> void:
|
|
var port := node.get_all_ports()[port_idx]
|
|
update_port(port)
|
|
|
|
|
|
## Connected to [member node]s [signal port_removed], queue_frees the [Node]
|
|
## used for the descriptor, as well as using [method GraphNode.set_slot] to remove the slot.
|
|
func _on_node_port_removed(port_idx: int) -> void:
|
|
set_slot(
|
|
port_idx,
|
|
false,
|
|
0,
|
|
Color.WHITE,
|
|
false,
|
|
0,
|
|
Color.WHITE,
|
|
)
|
|
|
|
get_child(port_idx).queue_free()
|
|
|
|
|
|
## Connected to [member node]s [signal ports_updated]. Remakes all of the ports
|
|
## + their descriptors whenever this is received to allow keeping the Renderers ports up to date.
|
|
func _on_node_ports_updated() -> void:
|
|
clear_all_slots()
|
|
for c in get_children():
|
|
c.queue_free()
|
|
|
|
for port in node.get_all_ports():
|
|
update_port(port)
|
|
|
|
await get_tree().process_frame
|
|
size = Vector2()
|
|
|
|
|
|
func _on_node_renamed(new_name: String) -> void:
|
|
title = new_name
|
|
|
|
|
|
func update_port(port: Port) -> void:
|
|
var descriptor_split := port.descriptor.split(":")
|
|
match descriptor_split[0]:
|
|
"button":
|
|
var button := Button.new()
|
|
add_child(button)
|
|
button.text = port.label
|
|
if port.port_type == DeckNode.PortType.OUTPUT:
|
|
button.pressed.connect(
|
|
func():
|
|
node.send(port.index_of_type, true)
|
|
)
|
|
#elif port.port_type == DeckNode.PortType.INPUT:
|
|
else:
|
|
button.pressed.connect(
|
|
func():
|
|
node._receive(port.index_of_type, true)
|
|
)
|
|
"field":
|
|
var line_edit := LineEdit.new()
|
|
add_child(line_edit)
|
|
if port.value:
|
|
line_edit.text = str(port.value)
|
|
line_edit.placeholder_text = port.label
|
|
port.value_callback = line_edit.get_text
|
|
line_edit.text_changed.connect(port.set_value)
|
|
"singlechoice":
|
|
var box := OptionButton.new()
|
|
if descriptor_split.slice(1).is_empty():
|
|
if port.value:
|
|
box.add_item(port.value)
|
|
else:
|
|
box.add_item(port.label)
|
|
else:
|
|
for item in descriptor_split.slice(1):
|
|
box.add_item(item)
|
|
add_child(box)
|
|
port.value_callback = func(): return box.get_item_text(box.get_selected_id())
|
|
if port.type == DeckType.Types.STRING:
|
|
box.item_selected.connect(
|
|
func(id: int):
|
|
port.set_value.call(box.get_item_text(box.get_selected_id()))
|
|
)
|
|
"codeblock":
|
|
var code_edit = CodeEdit.new()
|
|
add_child(code_edit)
|
|
if port.value:
|
|
code_edit.text = str(port.value)
|
|
code_edit.placeholder_text = port.label
|
|
port.value_callback = code_edit.get_text
|
|
code_edit.text_changed.connect(port.set_value.bind(code_edit.get_text))
|
|
code_edit.custom_minimum_size = Vector2(200, 100)
|
|
code_edit.size_flags_vertical = SIZE_EXPAND_FILL
|
|
"checkbox":
|
|
var cb := CheckBox.new()
|
|
add_child(cb)
|
|
if descriptor_split.size() > 1:
|
|
cb.button_pressed = true
|
|
if port.value is bool:
|
|
cb.button_pressed = port.value
|
|
cb.text = port.label
|
|
port.value_callback = cb.is_pressed
|
|
cb.toggled.connect(port.set_value)
|
|
"spinbox":
|
|
var sb := SpinBox.new()
|
|
add_child(sb)
|
|
if port.value != null:
|
|
sb.value = float(port.value)
|
|
if "unbounded" in descriptor_split:
|
|
sb.max_value = 99999
|
|
sb.allow_greater = true
|
|
sb.allow_lesser = true
|
|
if descriptor_split.size() > 2:
|
|
sb.step = float(descriptor_split[1])
|
|
else:
|
|
sb.step = 1.0
|
|
else:
|
|
sb.min_value = float(descriptor_split[1])
|
|
if descriptor_split.size() > 2:
|
|
sb.max_value = float(descriptor_split[2])
|
|
if descriptor_split.size() > 3:
|
|
sb.step = float(descriptor_split[3])
|
|
port.value_callback = sb.get_value
|
|
sb.value_changed.connect(port.set_value)
|
|
_:
|
|
var label := Label.new()
|
|
add_child(label)
|
|
label.text = port.label
|
|
if port.port_type == DeckNode.PortType.OUTPUT:
|
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
|
|
set_slot(
|
|
port.index,
|
|
port.port_type == DeckNode.PortType.INPUT,
|
|
port.type,
|
|
TYPE_COLORS[port.type],
|
|
port.port_type == DeckNode.PortType.OUTPUT,
|
|
port.type,
|
|
TYPE_COLORS[port.type],
|
|
PORT_USAGE_ICONS[port.usage_type],
|
|
PORT_USAGE_ICONS[port.usage_type],
|
|
)
|