lib group storage rework (#162)

saving decks that use lib groups will no longer save the whole file path to that library (at the expense of the structure needing to be the same)

also some ui/ux improvements:
- more menus in sidebar remember their collapsed state between deck/node switches
- adding a lib group will name the group node appropriately
- save dialog properly remembers the most recent path when invoked via ctrl+s and the deck hasn't been saved before

Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/162
Co-authored-by: Lera Elvoé <yagich@poto.cafe>
Co-committed-by: Lera Elvoé <yagich@poto.cafe>
This commit is contained in:
Lera Elvoé 2024-05-26 14:13:42 +00:00 committed by yagich
parent 32bda994f6
commit f720efcc72
7 changed files with 85 additions and 59 deletions

View file

@ -175,6 +175,7 @@ func add_lib_group_node(type: String) -> DeckNode:
lib.group_output_node = node._id
continue
group_node.init_io()
group_node.rename(NodeDB.get_library_descriptor(type).name)
return group_node
@ -696,7 +697,7 @@ static func from_dict(data: Dictionary, path: String = "") -> Deck:
group = DeckHolder.add_group_from_dict(group_data, group_id, group_instance_id, deck.id)
else:
group = DeckHolder.add_lib_instance(
node.group_id.get_file().trim_suffix(".deck"),
node.group_id.get_file(),
deck.id,
node.group_instance_id
)

View file

@ -153,8 +153,8 @@ static func add_lib_instance(type: String, to_deck: String, instance_id: String
var nd: NodeDB.NodeDescriptor = NodeDB.libraries[type]
var path := nd.script_path
var deck_data: Dictionary
if lib_groups.has(path):
deck_data = (lib_groups[path].values()[0] as Deck).to_dict()
if lib_groups.has(type):
deck_data = (lib_groups[type].values()[0] as Deck).to_dict()
else:
if not NodeDB.libraries.has(type):
return null
@ -165,17 +165,17 @@ static func add_lib_instance(type: String, to_deck: String, instance_id: String
deck_data = JSON.parse_string(f.get_as_text())
var instances := lib_groups.get(path, {}) as Dictionary
var instances := lib_groups.get(type, {}) as Dictionary
var deck := Deck.from_dict(deck_data)
deck.id = path
deck.id = type
deck.instance_id = instance_id
deck.is_group = true
deck.is_library = true
deck._belonging_to = to_deck
instances[instance_id] = deck
lib_groups[path] = instances
lib_groups[type] = instances
print_verbose("DeckHolder: added lib group %s::%s, id %s" % [deck.id, deck.instance_id, deck.get_instance_id()])
return deck
@ -296,20 +296,20 @@ static func pre_exit_cleanup() -> void:
static func send_event(event_name: StringName, event_data: Dictionary = {}) -> void:
for deck_id: String in decks:
if decks[deck_id] is Deck:
var deck: Deck = (decks[deck_id] as Deck)
var deck: Deck = decks[deck_id]
if not is_instance_valid(deck):
continue
deck.send_event(event_name, event_data)
else:
for deck_instance_id: String in decks[deck_id]:
var deck: Deck = (decks[deck_id][deck_instance_id] as Deck)
var deck: Deck = decks[deck_id][deck_instance_id]
if not is_instance_valid(deck):
continue
deck.send_event(event_name, event_data)
for lib_group_id: String in lib_groups:
for lib_instance_id: String in lib_groups[lib_group_id]:
var deck: Deck = (lib_groups[lib_group_id][lib_instance_id] as Deck)
var deck: Deck = lib_groups[lib_group_id][lib_instance_id]
if not is_instance_valid(deck):
continue
deck.send_event(event_name, event_data)

View file

@ -89,7 +89,7 @@ static func create_lib_descriptors(path: String) -> void:
if not deck.deck.has("library"):
current_file = dir.get_next()
continue
var type := current_file.trim_suffix(".deck")
var type := current_file
if nodes.has(type):
DeckHolder.logger.toast_error("Library group '%s' collides with a node with the same type." % type)
current_file = dir.get_next()
@ -113,7 +113,8 @@ static func create_lib_descriptors(path: String) -> void:
true,
true,
)
libraries[descriptor.type] = descriptor
descriptor.added_by_library = path
libraries[type] = descriptor
current_file = dir.get_next()
@ -200,6 +201,10 @@ static func is_library(type: String) -> bool:
return libraries.has(type)
static func get_library_descriptor(type: String) -> NodeDescriptor:
return libraries.get(type, null) as NodeDescriptor
## Returns a capitalized category string.
static func get_category_capitalization(category: String) -> String:
return CATEGORY_CAPITALIZATION.get(category, category.capitalize())
@ -220,8 +225,10 @@ class NodeDescriptor:
var category: String
## Whether this [DeckNode] reference will appear when searching. See [member DeckNode.appears_in_search].
var appears_in_search: bool
## Whether this describes a library group.
var is_library: bool
## If [member is_library] is [code]true[/code], this is the index that adds this library.
var added_by_library: String
## Stores the path to this node's script for later instantiation.
var script_path: String
@ -258,6 +265,7 @@ class NodeDescriptor:
"category": category,
"appears_in_search": appears_in_search,
"is_library": is_library,
"added_by_library": added_by_library,
}
return d
@ -274,5 +282,5 @@ class NodeDescriptor:
data.get("appears_in_search", false),
data.get("is_library", false)
)
nd.added_by_library = data.get("added_by_library", "")
return nd

View file

@ -69,37 +69,41 @@ func make_unique() -> Deck:
func setup_connections() -> void:
input_node.ports_updated.connect(recalculate_ports)
output_node.ports_updated.connect(recalculate_ports)
if input_node != null:
input_node.ports_updated.connect(recalculate_ports)
if output_node != null:
output_node.ports_updated.connect(recalculate_ports)
func recalculate_ports() -> void:
var _values := extra_port_values.duplicate()
ports.clear()
for output_port: Port in output_node.get_input_ports().slice(0, output_node.get_input_ports().size() - 1):
var port := add_output_port(
output_port.type,
output_port.label,
output_port.descriptor,
output_port.usage_type,
)
if _values.size() - 1 < port.index:
continue
if output_node != null:
for output_port: Port in output_node.get_input_ports().slice(0, output_node.get_input_ports().size() - 1):
var port := add_output_port(
output_port.type,
output_port.label,
output_port.descriptor,
output_port.usage_type,
)
if _values.size() - 1 < port.index:
continue
port.set_value(_values[port.index])
port.set_value(_values[port.index])
for input_port: Port in input_node.get_output_ports().slice(0, input_node.get_output_ports().size() - 1):
var port := add_input_port(
input_port.type,
input_port.label,
input_port.descriptor,
input_port.usage_type,
)
if _values.size() - 1 < port.index:
continue
if input_node != null:
for input_port: Port in input_node.get_output_ports().slice(0, input_node.get_output_ports().size() - 1):
var port := add_input_port(
input_port.type,
input_port.label,
input_port.descriptor,
input_port.usage_type,
)
if _values.size() - 1 < port.index:
continue
port.set_value(_values[port.index])
port.set_value(_values[port.index])
extra_ports.clear()
extra_port_values.clear()

View file

@ -40,17 +40,17 @@ func add_category(category_name: String) -> void:
## Add an item to a category.
func add_category_item(category: String, item: String, tooltip: String = "", favorite: bool = false, library: bool = false) -> void:
func add_category_item(category: String, item: String, type: String, tooltip: String = "", favorite: bool = false, library: bool = false) -> void:
var c: Category = categories[category]
c.add_item(item, tooltip, favorite, library)
c.add_item(item, tooltip, type, favorite, library)
## Wrapper around [method add_category_item] and [method add_category]. Adds an item to a [param category], creating the category if it doesn't exist yet.
func add_item(category: String, item: String, tooltip: String = "", favorite: bool = false, library: bool = false) -> void:
func add_item(category: String, item: String, type: String, tooltip: String = "", favorite: bool = false, library: bool = false) -> void:
if not categories.has(category):
add_category(category)
add_category_item(category, item, tooltip, favorite, library)
add_category_item(category, item, type, tooltip, favorite, library)
## Get a [AddNodeMenu.Category] node by its' identifier.
@ -74,7 +74,7 @@ func search(text: String) -> void:
return
for nd in search_results:
add_item(nd.category, nd.name, nd.description, NodeDB.is_node_favorite(nd.type), nd.is_library)
add_item(nd.category, nd.name, nd.type, nd.description, NodeDB.is_node_favorite(nd.type), nd.is_library)
var c := get_category(nd.category)
c.set_item_metadata(c.get_item_count() - 1, "type", nd.type)
c.set_item_metadata(c.get_item_count() - 1, "node_descriptor", weakref(nd))
@ -231,8 +231,8 @@ class Category extends VBoxContainer:
## Add an item to the category.
func add_item(p_name: String, p_tooltip: String, p_favorite: bool = false, p_library: bool = false) -> void:
var item := CategoryItem.new(p_name, p_tooltip, p_favorite, p_library)
func add_item(p_name: String, p_tooltip: String, p_type: String, p_favorite: bool = false, p_library: bool = false) -> void:
var item := CategoryItem.new(p_name, p_tooltip, p_favorite, p_library, p_type)
item.favorite_toggled.connect(
func(toggled: bool):
item_favorite_button_toggled.emit(item.get_index(), toggled)
@ -321,7 +321,7 @@ class CategoryItem extends HBoxContainer:
signal favorite_toggled(toggled: bool)
func _init(p_name: String, p_tooltip: String, p_favorite: bool, p_library: bool) -> void:
func _init(p_name: String, p_tooltip: String, p_favorite: bool, p_library: bool, p_type: String) -> void:
fav_button = Button.new()
fav_button.icon = FAVORITE_ICON if p_favorite else NON_FAVORITE_ICON
fav_button.toggle_mode = true
@ -358,11 +358,14 @@ class CategoryItem extends HBoxContainer:
inner_hb.add_child(name_button)
if p_library:
var nd := NodeDB.get_library_descriptor(p_type)
group_texture_rect = TextureRect.new()
group_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
group_texture_rect.custom_minimum_size = Vector2(12, 12)
group_texture_rect.texture = GROUP_ICON
group_texture_rect.tooltip_text = "This is a library group. It will create a group node."
group_texture_rect.tooltip_text += "\nAdded by %s" % nd.added_by_library
inner_hb.add_child(group_texture_rect)
var m := MarginContainer.new()
m.add_theme_constant_override(&"margin_right", 6)

View file

@ -324,7 +324,7 @@ func close_tab(tab: int) -> void:
func open_save_dialog(path: String) -> void:
file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
file_dialog.title = "Save a Deck"
file_dialog.current_path = path
file_dialog.current_path = path if not path.is_empty() else recent_path
_deck_to_save = weakref(get_active_deck())
file_dialog.popup_centered()
file_dialog.file_selected.connect(_on_file_dialog_save_file, CONNECT_ONE_SHOT)
@ -451,7 +451,8 @@ func save_tab(tab: int) -> void:
var renderer := tab_container.get_content(tab) as DeckRendererGraphEdit
var deck := renderer.deck
if deck.save_path.is_empty():
open_save_dialog("res://")
#open_save_dialog("res://") # what the fuck?
open_save_dialog(tab_container.get_tab_metadata(tab, "path", recent_path))
else:
var json := JSON.stringify(deck.to_dict(), "\t")
var f := FileAccess.open(deck.save_path, FileAccess.WRITE)
@ -485,7 +486,12 @@ func _on_deck_renderer_group_enter_requested(group_id: String) -> void:
var deck_renderer: DeckRendererGraphEdit = DECK_SCENE.instantiate()
deck_renderer.deck = group_deck
deck_renderer.initialize_from_deck()
var tab := tab_container.add_content(deck_renderer, "(g) %s" % group_id.left(8))
var title := ""
if group_deck.is_library:
title = "(L) %s" % group_deck.id
else:
title = "(G) %s" % group_id.left(8)
var tab := tab_container.add_content(deck_renderer, title)
tab_container.set_tab_metadata(tab, "id", group_id)
tab_container.set_tab_metadata(tab, "group", true)
deck_renderer.group_enter_requested.connect(_on_deck_renderer_group_enter_requested)

View file

@ -136,6 +136,8 @@ func set_edited_node(id: String = "") -> void:
static func create_menu(title: String, id: String, default_collapsed: bool = false) -> AccordionMenu:
if id.is_empty():
id = title
var res := AccordionMenu.new()
res.set_title(title)
res.collapsed = collapsed_menus.get(id, default_collapsed)
@ -190,14 +192,15 @@ class DeckInspector:
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_menu := AccordionMenu.new()
#lib_menu.set_title("Library Group")
var lib_menu := Sidebar.create_menu("Library Group", "lib_group", true)
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."
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 in search results."
else:
lib_group_text = "This deck is a group."
@ -247,8 +250,9 @@ class GroupDescriptorsInspector:
func _init(p_deck: Deck) -> void:
deck = p_deck
menu = AccordionMenu.new()
menu.set_title("Group Inputs/Outputs")
#menu = AccordionMenu.new()
#menu.set_title("Group Inputs/Outputs")
menu = Sidebar.create_menu("Group Inputs/Outputs", "group_io", true)
inputs_menu = Sidebar.create_menu("Inputs", "group_inputs", true)
outputs_menu = Sidebar.create_menu("Outputs", "group_outputs", true)
@ -471,14 +475,15 @@ class NodeInspector:
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")
var ports_menu := Sidebar.create_menu("Output Ports" if is_output else "Input Ports", "", false)
ports_menu.draw_background = true
for port in ports:
var acc := AccordionMenu.new()
#var acc := AccordionMenu.new()
var id = "output_%s_menu" if is_output else "input_%s_menu"
var acc := Sidebar.create_menu("Port %s" % port.index_of_type, id % port.index_of_type, true)
acc.draw_background = true
acc.set_title("Port %s" % port.index_of_type)
#acc.set_title("Port %s" % port.index_of_type)
var label_label := DeckInspector.create_label("Name: %s" % port.label)
acc.add_child(label_label)
@ -505,5 +510,4 @@ class NodeInspector:
acc.add_child(port_hb)
ports_menu.add_child(acc)
ports_menu.collapsed = true
nodes.append(ports_menu)