miggor-StreamGraph/graph_node_renderer/sidebar/sidebar.gd
Lera Elvoé 240750c48e library groups (#151)
~~cl0ses #51~~
~~cl0ses #93~~
~~cl0ses #98~~
~~cl0ses #150~~

another change in this PR: the Deck and DeckNode classes now use manual memory management.

Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/151
Co-authored-by: Lera Elvoé <yagich@poto.cafe>
Co-committed-by: Lera Elvoé <yagich@poto.cafe>
2024-05-08 07:43:45 +00:00

339 lines
9.9 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 PanelContainer
class_name Sidebar
@onready var deck_menu: AccordionMenu = %Deck
@onready var node_menu: AccordionMenu = %Node
@onready var node_list_menu: AccordionMenu = %"Node List"
const SYSTEM_CODE_FONT = preload("res://graph_node_renderer/system_code_font.tres")
var edited_deck_id: String
var edited_node_id: String
var deck_inspector: DeckInspector
var node_inspector: NodeInspector
signal go_to_node_requested(node_id: String)
func _ready() -> void:
set_edited_deck()
func set_edited_deck(id: String = "") -> void:
if edited_deck_id == id and not id.is_empty():
return
edited_deck_id = id
set_edited_node("")
for i in deck_menu.get_children():
i.queue_free()
if id.is_empty():
var label := Label.new()
label.autowrap_mode = TextServer.AUTOWRAP_WORD
label.text = "There is no open Deck. Open or create one to edit its properties."
deck_menu.add_child(label)
deck_inspector = null
return
deck_inspector = DeckInspector.new(id)
for i in deck_inspector.nodes:
deck_menu.add_child(i)
func refresh_node_list(_unused = null) -> void:
for i in node_list_menu.get_children():
i.queue_free()
for node_id: String in DeckHolder.get_deck(edited_deck_id).nodes:
var node := DeckHolder.get_deck(edited_deck_id).get_node(node_id)
var b := Button.new()
b.flat = true
b.text = "\"%s\"" % node.name
b.pressed.connect(
func():
go_to_node_requested.emit(node._id)
)
b.size_flags_horizontal = Control.SIZE_EXPAND_FILL
b.tooltip_text = "Click to focus this node in the graph"
b.alignment = HORIZONTAL_ALIGNMENT_LEFT
b.clip_text = true
var cb := Button.new()
cb.text = "Copy ID"
cb.pressed.connect(
func():
DisplayServer.clipboard_set(node._id)
)
#Util.safe_connect(node.renamed,
#func(new_name: String):
# TODO: bad. if the node (deck node or button, whichever)
# gets removed the captures get invalidated.
# maybe dont use lambda here
#b.text = "\"%s\"" % node.name
#)
#Util.safe_connect(node.renamed, _set_button_text.bind(b))
# this is probably bad too, but its the only one that worked so far.
Util.safe_connect(node.renamed, refresh_node_list)
var hb := HBoxContainer.new()
hb.add_child(b)
hb.add_child(cb)
node_list_menu.add_child(hb)
#func _set_button_text(new_text: String, button: Button) -> void:
# this was also bad. "Cannot convert argument 2 from Object to Object" 🙃
#button.text = "\"%s\"" % new_text
func set_edited_node(id: String = "") -> void:
if edited_node_id == id and not id.is_empty():
return
edited_node_id = id
for i in node_menu.get_children():
#i.queue_free()
i.free()
if id.is_empty():
var label := Label.new()
label.autowrap_mode = TextServer.AUTOWRAP_WORD
label.text = "There is no Node selected (or multiple are selected). Select a single Node to edit its properties."
node_menu.add_child(label)
node_menu.draw_tree = false
node_inspector = null
return
#await get_tree().process_frame
node_menu.draw_tree = true
node_inspector = NodeInspector.new(edited_deck_id, id)
node_inspector.go_to_node_requested.connect(
func():
go_to_node_requested.emit(edited_node_id)
)
for i in node_inspector.nodes:
node_menu.add_child(i)
DeckHolder.get_deck(edited_deck_id).get_node(id).about_to_free.connect(
func():
set_edited_node()
)
#DeckHolder.get_deck(edited_deck_id).node_removed.connect(
#func(node: DeckNode):
#if node._id == edited_node_id:
#set_edited_node()
#)
class Inspector extends HBoxContainer:
func _init(text: String, editor: Control = null) -> void:
var _label = Label.new()
_label.text = text
_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
add_child(_label)
if editor:
editor.size_flags_horizontal = Control.SIZE_EXPAND_FILL
add_child(editor)
class DeckInspector:
var ref: WeakRef
var nodes: Array[Control]
func add_inspector(text: String, control: Control = null) -> void:
nodes.append(Inspector.new(text, control))
func create_label(text: String) -> Label:
var l := Label.new()
l.text = text
l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
return l
func create_hb_label(text: String, control: Control) -> HBoxContainer:
var hb := HBoxContainer.new()
control.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hb.add_child(create_label(text))
hb.add_child(control)
return hb
func _init(id: String) -> void:
ref = weakref(DeckHolder.get_deck(id))
var deck: Deck = ref.get_ref() as Deck
var lib_menu := AccordionMenu.new()
lib_menu.set_title("Library Group")
var lib_group_text: String
if deck.is_library:
lib_group_text = "This deck is open as a library group. You may not edit it here.\nTo edit it, you have to open the file it's in."
elif not deck.is_group:
lib_group_text = "You may save this deck as a Library Group to reuse it in future decks.\nYou may edit how it will appear."
else:
lib_group_text = "This deck is a group."
var l := create_label(lib_group_text)
l.autowrap_mode = TextServer.AUTOWRAP_WORD
l.custom_minimum_size.x = 40
lib_menu.add_child(l)
lib_menu.set_ignore_child_lines(0)
var name_field := LineEdit.new()
name_field.placeholder_text = "Name"
name_field.text = deck.lib_name
name_field.text_changed.connect(
func(new_text: String):
deck.lib_name = new_text
)
name_field.editable = not deck.is_group
lib_menu.add_child(create_hb_label("Library name:", name_field))
var desc_field := TextEdit.new()
desc_field.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
desc_field.placeholder_text = "Description"
desc_field.text = deck.lib_description
desc_field.text_changed.connect(
func():
deck.lib_description = desc_field.text
)
desc_field.editable = not deck.is_group
desc_field.custom_minimum_size.y = 100
desc_field.custom_minimum_size.x = 100
lib_menu.add_child(create_hb_label("Library description:", desc_field))
nodes.append(lib_menu)
class NodeInspector:
var ref: WeakRef
var nodes: Array[Control]
var _name_field: LineEdit
signal go_to_node_requested()
var DESCRIPTOR_SCENES := {
"button": load("res://graph_node_renderer/descriptors/button_descriptor.tscn"),
"field": load("res://graph_node_renderer/descriptors/field_descriptor.tscn"),
"singlechoice": load("res://graph_node_renderer/descriptors/single_choice_descriptor.tscn"),
"codeblock": load("res://graph_node_renderer/descriptors/codeblock_descriptor.tscn"),
"checkbox": load("res://graph_node_renderer/descriptors/check_box_descriptor.tscn"),
"spinbox": load("res://graph_node_renderer/descriptors/spin_box_descriptor.tscn"),
"label": load("res://graph_node_renderer/descriptors/label_descriptor.tscn"),
}
func add_inspector(text: String, control: Control = null) -> void:
nodes.append(Inspector.new(text, control))
func create_label(text: String) -> Label:
var l := Label.new()
l.text = text
l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
return l
func _name_field_rename(new_name: String) -> void:
if _name_field.has_focus():
return
_name_field.text = new_name
func _init(deck_id: String, id: String) -> void:
ref = weakref(DeckHolder.get_deck(deck_id).get_node(id))
var node: DeckNode = ref.get_ref() as DeckNode
var type_label := create_label(node.node_type)
var type_label_settings := LabelSettings.new()
type_label_settings.font = SYSTEM_CODE_FONT
type_label_settings.font_size = 14
type_label.label_settings = type_label_settings
var copy_button := Button.new()
copy_button.text = "Copy"
copy_button.pressed.connect(
func():
DisplayServer.clipboard_set(node.node_type)
)
var hb := HBoxContainer.new()
hb.add_child(type_label)
hb.add_child(copy_button)
add_inspector("Node Type:", hb)
_name_field = LineEdit.new()
_name_field.placeholder_text = "Node name"
_name_field.text = node.name
_name_field.text_changed.connect(node.rename)
_name_field.editable = not node._belonging_to.is_library
node.renamed.connect(_name_field_rename)
add_inspector("Node name:", _name_field)
var focus_button := Button.new()
focus_button.text = "Focus node"
focus_button.pressed.connect(
func():
go_to_node_requested.emit()
)
focus_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
nodes.append(focus_button)
add_port_menu(node.get_input_ports(), node)
add_port_menu(node.get_output_ports(), node)
func add_port_menu(ports: Array[Port], node: DeckNode) -> void:
if ports.is_empty():
return
var ports_menu := AccordionMenu.new()
ports_menu.draw_background = true
var is_output := ports[0].port_type == DeckNode.PortType.OUTPUT
ports_menu.set_title("Output Ports" if is_output else "Input Ports")
for port in ports:
var acc := AccordionMenu.new()
acc.draw_background = true
acc.set_title("Port %s" % port.index_of_type)
var label_label := create_label("Name: %s" % port.label)
acc.add_child(label_label)
var type_label := create_label("Type: %s" % DeckType.type_str(port.type).to_lower())
acc.add_child(type_label)
var usage_is_both := port.usage_type == Port.UsageType.BOTH
var usage_text = "Both (Value Request or Trigger)" if usage_is_both else Port.UsageType.keys()[port.usage_type].capitalize()
var usage_label := create_label("Usage: %s" % usage_text)
acc.add_child(usage_label)
var descriptor_split := port.descriptor.split(":")
if DESCRIPTOR_SCENES.has(descriptor_split[0]):
var port_hb := HBoxContainer.new()
var value_label := create_label("Value:")
port_hb.add_child(value_label)
var desc: DescriptorContainer = DESCRIPTOR_SCENES[descriptor_split[0]].instantiate()
desc.ready.connect(
func():
desc.set_up_from_port(port, node, false)
)
port_hb.add_child(desc)
acc.add_child(port_hb)
ports_menu.add_child(acc)
ports_menu.collapsed = true
nodes.append(ports_menu)