mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
Ero's Doc Sprint 1 (#5)
Co-authored-by: Eroax <eroaxe.business@gmail.com> Reviewed-on: https://codeberg.org/Eroax/Re-DotDeck/pulls/5 Co-authored-by: Eroax <eroax@noreply.codeberg.org> Co-committed-by: Eroax <eroax@noreply.codeberg.org>
This commit is contained in:
parent
eeb509cae0
commit
2264890113
5 changed files with 144 additions and 55 deletions
|
@ -1,15 +1,25 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
class_name NodeDB_
|
||||||
|
|
||||||
|
## Filepath used for referencing all [DeckNode] Files from.
|
||||||
const BASE_NODE_PATH := "res://classes/deck/nodes/"
|
const BASE_NODE_PATH := "res://classes/deck/nodes/"
|
||||||
|
## Filepath where an Index of [NodeDB_.NodeDescriptor]s are saved to avoid reloading
|
||||||
|
## everything each run.
|
||||||
|
## @experimental
|
||||||
const NODE_INDEX_CACHE_PATH := "user://nodes_index.json"
|
const NODE_INDEX_CACHE_PATH := "user://nodes_index.json"
|
||||||
|
## Filepath where the the list of Favorite Nodes is stored.
|
||||||
const FAVORITE_NODES_PATH := "user://favorite_nodes.json"
|
const FAVORITE_NODES_PATH := "user://favorite_nodes.json"
|
||||||
|
|
||||||
|
## [Array] used for storing all the "Favorite" Nodes that were set in the [AddNodeMenu]
|
||||||
var favorite_nodes: Array[String]
|
var favorite_nodes: Array[String]
|
||||||
|
|
||||||
# Dictionary[node_type, NodeDescriptor]
|
# Dictionary[node_type, NodeDescriptor]
|
||||||
|
## [Dictionary] filled with node_type, or [String] keys. Correlating to
|
||||||
|
## [NodeDB_.NodeDescriptor]'s. Essentially where all the nodes are loaded in for data reference.
|
||||||
var nodes: Dictionary = {}
|
var nodes: Dictionary = {}
|
||||||
|
|
||||||
|
## Loads in all the [DeckNode]s from [member BASE_NODE_PATH]. Or if a working
|
||||||
|
## cache exists at [member NODE_INDEX_CACHE_PATH] that takes priority
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
load_favorites()
|
load_favorites()
|
||||||
#if load_node_index():
|
#if load_node_index():
|
||||||
|
@ -41,14 +51,14 @@ func _init() -> void:
|
||||||
|
|
||||||
save_node_index()
|
save_node_index()
|
||||||
|
|
||||||
|
## Helper Function that instances a [DeckNode] based off of it's [member DeckNode.node_type]
|
||||||
func instance_node(type: String) -> DeckNode:
|
func instance_node(type: String) -> DeckNode:
|
||||||
if !nodes.has(type):
|
if !nodes.has(type):
|
||||||
return null
|
return null
|
||||||
|
|
||||||
return load(nodes[type]["script_path"]).new()
|
return load(nodes[type]["script_path"]).new()
|
||||||
|
|
||||||
|
## Handles Saving the Index of all loaded nodes to [member NODE_INDEX_CACHE_PATH]
|
||||||
func save_node_index() -> void:
|
func save_node_index() -> void:
|
||||||
var d := {}
|
var d := {}
|
||||||
for node_type in nodes:
|
for node_type in nodes:
|
||||||
|
@ -59,7 +69,8 @@ func save_node_index() -> void:
|
||||||
var f := FileAccess.open(NODE_INDEX_CACHE_PATH, FileAccess.WRITE)
|
var f := FileAccess.open(NODE_INDEX_CACHE_PATH, FileAccess.WRITE)
|
||||||
f.store_string(json)
|
f.store_string(json)
|
||||||
|
|
||||||
|
## Loads the Node Index from [member NODE_INDEX_CACHE_PATH] adding all of the
|
||||||
|
## [NodeDB_.NodeDescriptor]s in it to the [member nodes] [Dictionary]
|
||||||
func load_node_index() -> bool:
|
func load_node_index() -> bool:
|
||||||
var f := FileAccess.open(NODE_INDEX_CACHE_PATH, FileAccess.READ)
|
var f := FileAccess.open(NODE_INDEX_CACHE_PATH, FileAccess.READ)
|
||||||
if f == null:
|
if f == null:
|
||||||
|
@ -79,7 +90,8 @@ func load_node_index() -> bool:
|
||||||
print("node index file exists, loaded")
|
print("node index file exists, loaded")
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
## Sets a specific [member DeckNode.node_type] to be a "favorite" for use in
|
||||||
|
## [AddNodeMenu]. Then stores the updated list of favorites at [member FAVORITE_NODES_PATH]
|
||||||
func set_node_favorite(node_type: String, favorite: bool) -> void:
|
func set_node_favorite(node_type: String, favorite: bool) -> void:
|
||||||
if (favorite && node_type in favorite_nodes) || (!favorite && !(node_type in favorite_nodes)):
|
if (favorite && node_type in favorite_nodes) || (!favorite && !(node_type in favorite_nodes)):
|
||||||
return
|
return
|
||||||
|
@ -92,7 +104,7 @@ func set_node_favorite(node_type: String, favorite: bool) -> void:
|
||||||
var f := FileAccess.open(FAVORITE_NODES_PATH, FileAccess.WRITE)
|
var f := FileAccess.open(FAVORITE_NODES_PATH, FileAccess.WRITE)
|
||||||
f.store_string(JSON.stringify(favorite_nodes, "\t"))
|
f.store_string(JSON.stringify(favorite_nodes, "\t"))
|
||||||
|
|
||||||
|
## Loads the list of Favorite [memeber DeckNode.node_type]s from [member FAVORITE_NODES_PATH]
|
||||||
func load_favorites() -> void:
|
func load_favorites() -> void:
|
||||||
var f := FileAccess.open(FAVORITE_NODES_PATH, FileAccess.READ)
|
var f := FileAccess.open(FAVORITE_NODES_PATH, FileAccess.READ)
|
||||||
if !f:
|
if !f:
|
||||||
|
@ -101,22 +113,32 @@ func load_favorites() -> void:
|
||||||
favorite_nodes.clear()
|
favorite_nodes.clear()
|
||||||
favorite_nodes.assign(data)
|
favorite_nodes.assign(data)
|
||||||
|
|
||||||
|
## Returns [code]true[/code] if the specified [member DeckNode.node_type] is marked Favorite
|
||||||
|
## by the user.
|
||||||
func is_node_favorite(node_type: String) -> bool:
|
func is_node_favorite(node_type: String) -> bool:
|
||||||
return node_type in favorite_nodes
|
return node_type in favorite_nodes
|
||||||
|
|
||||||
|
## Used for storing the shorthand data of a [DeckNode].
|
||||||
|
##
|
||||||
|
## Allows for more simply storing [DeckNode]s properties without needing to
|
||||||
|
## keep an instance
|
||||||
class NodeDescriptor:
|
class NodeDescriptor:
|
||||||
|
## Default Name of the [DeckNode] type this is storing properties of.
|
||||||
var name: String
|
var name: String
|
||||||
|
## The [member DeckNode.node_type] of the [DeckNode] this is based off of.
|
||||||
var type: String
|
var type: String
|
||||||
|
## The description of the [DeckNode] reference [member DeckNode.description]
|
||||||
var description: String
|
var description: String
|
||||||
|
## The description of the [DeckNode] reference [member DeckNode.aliases]
|
||||||
var aliases: String
|
var aliases: String
|
||||||
|
## The description of the [DeckNode] reference [member DeckNode.category]
|
||||||
var category: String
|
var category: String
|
||||||
|
## The description of the [DeckNode] reference [member DeckNode.appears_in_search]
|
||||||
var appears_in_search: bool
|
var appears_in_search: bool
|
||||||
|
|
||||||
|
## Stores the path to this nodes [Script] in res:// for later instantiation.
|
||||||
var script_path: String
|
var script_path: String
|
||||||
|
|
||||||
|
|
||||||
func _init(
|
func _init(
|
||||||
p_script_path: String,
|
p_script_path: String,
|
||||||
p_name: String,
|
p_name: String,
|
||||||
|
@ -135,7 +157,7 @@ class NodeDescriptor:
|
||||||
category = p_category
|
category = p_category
|
||||||
appears_in_search = p_appears_in_search
|
appears_in_search = p_appears_in_search
|
||||||
|
|
||||||
|
## Converts all the properties in this [NodeDB_.NodeDescriptor] to a [Dictionary]. Format - {propery : value}
|
||||||
func to_dictionary() -> Dictionary:
|
func to_dictionary() -> Dictionary:
|
||||||
var d := {
|
var d := {
|
||||||
"name": name,
|
"name": name,
|
||||||
|
@ -148,7 +170,7 @@ class NodeDescriptor:
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
## Creates a [NodeDB_.NodeDescriptor] from a given [Dictionary] of properties.
|
||||||
static func from_dictionary(data: Dictionary) -> NodeDescriptor:
|
static func from_dictionary(data: Dictionary) -> NodeDescriptor:
|
||||||
var nd := NodeDescriptor.new(
|
var nd := NodeDescriptor.new(
|
||||||
data.get("script_path", ""),
|
data.get("script_path", ""),
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
extends Control
|
extends Control
|
||||||
|
class_name DeckHolderRenderer
|
||||||
|
|
||||||
|
## Renderer class for [DeckHolder]
|
||||||
|
##
|
||||||
|
## Entry point for the [GraphEdit] based Renderer
|
||||||
|
|
||||||
|
## Reference to the base scene for [DeckRendererGraphEdit]
|
||||||
const DECK_SCENE := preload("res://graph_node_renderer/deck_renderer_graph_edit.tscn")
|
const DECK_SCENE := preload("res://graph_node_renderer/deck_renderer_graph_edit.tscn")
|
||||||
|
|
||||||
|
## Reference to the main windows [TabContainerCustom]
|
||||||
@onready var tab_container: TabContainerCustom = %TabContainerCustom as TabContainerCustom
|
@onready var tab_container: TabContainerCustom = %TabContainerCustom as TabContainerCustom
|
||||||
|
## Reference to the [FileDialog] used for File operations through the program.
|
||||||
@onready var file_dialog: FileDialog = $FileDialog
|
@onready var file_dialog: FileDialog = $FileDialog
|
||||||
|
|
||||||
|
## Enum for storing the Options in the "File" PopupMenu.
|
||||||
enum FileMenuId {
|
enum FileMenuId {
|
||||||
NEW,
|
NEW,
|
||||||
OPEN,
|
OPEN,
|
||||||
|
@ -13,6 +22,7 @@ enum FileMenuId {
|
||||||
CLOSE = 6,
|
CLOSE = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## Weak Reference to the Deck that is currently going to be saved.
|
||||||
var _deck_to_save: WeakRef
|
var _deck_to_save: WeakRef
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +37,8 @@ func _ready() -> void:
|
||||||
|
|
||||||
file_dialog.canceled.connect(disconnect_file_dialog_signals)
|
file_dialog.canceled.connect(disconnect_file_dialog_signals)
|
||||||
|
|
||||||
|
## Called when the File button in the [MenuBar] is pressed with the [param id]
|
||||||
|
## of the button within it that was pressed.
|
||||||
func _on_file_id_pressed(id: int) -> void:
|
func _on_file_id_pressed(id: int) -> void:
|
||||||
match id:
|
match id:
|
||||||
FileMenuId.NEW:
|
FileMenuId.NEW:
|
||||||
|
@ -41,7 +52,7 @@ func _on_file_id_pressed(id: int) -> void:
|
||||||
FileMenuId.CLOSE:
|
FileMenuId.CLOSE:
|
||||||
close_current_tab()
|
close_current_tab()
|
||||||
|
|
||||||
|
## Adds an empty [DeckRendererGraphEdit] with a corresponding [Deck] for it's data.
|
||||||
func add_empty_deck() -> void:
|
func add_empty_deck() -> void:
|
||||||
var deck := DeckHolder.add_empty_deck()
|
var deck := DeckHolder.add_empty_deck()
|
||||||
var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate()
|
var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate()
|
||||||
|
@ -50,11 +61,12 @@ func add_empty_deck() -> void:
|
||||||
tab_container.set_tab_metadata(tab_container.get_current_tab(), deck)
|
tab_container.set_tab_metadata(tab_container.get_current_tab(), deck)
|
||||||
inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck))
|
inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck))
|
||||||
|
|
||||||
|
## Closes the current tab in [member tab_container]
|
||||||
func close_current_tab() -> void:
|
func close_current_tab() -> void:
|
||||||
tab_container.close_tab(tab_container.get_current_tab())
|
tab_container.close_tab(tab_container.get_current_tab())
|
||||||
|
|
||||||
|
## Opens [member file_dialog] with the mode [member FileDialog.FILE_MODE_SAVE_FILE]
|
||||||
|
## as well as getting a weakref to the active [Deck]
|
||||||
func open_save_dialog(path: String) -> void:
|
func open_save_dialog(path: String) -> void:
|
||||||
file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
|
file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
|
||||||
file_dialog.title = "Save a Deck"
|
file_dialog.title = "Save a Deck"
|
||||||
|
@ -63,7 +75,8 @@ func open_save_dialog(path: String) -> void:
|
||||||
file_dialog.popup_centered()
|
file_dialog.popup_centered()
|
||||||
file_dialog.file_selected.connect(_on_file_dialog_save_file, CONNECT_ONE_SHOT)
|
file_dialog.file_selected.connect(_on_file_dialog_save_file, CONNECT_ONE_SHOT)
|
||||||
|
|
||||||
|
## Opens [member file_dialog] with the mode [FileDialog.FILE_MODE_OPEN_FILES]
|
||||||
|
## with the supplied [param path]
|
||||||
func open_open_dialog(path: String) -> void:
|
func open_open_dialog(path: String) -> void:
|
||||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILES
|
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILES
|
||||||
file_dialog.title = "Open Deck(s)"
|
file_dialog.title = "Open Deck(s)"
|
||||||
|
@ -71,7 +84,8 @@ func open_open_dialog(path: String) -> void:
|
||||||
file_dialog.popup_centered()
|
file_dialog.popup_centered()
|
||||||
file_dialog.files_selected.connect(_on_file_dialog_open_files, CONNECT_ONE_SHOT)
|
file_dialog.files_selected.connect(_on_file_dialog_open_files, CONNECT_ONE_SHOT)
|
||||||
|
|
||||||
|
## Connected to [signal FileDialog.save_file] on [member file_dialog].
|
||||||
|
## Saves the selected [Deck] if it still exists.
|
||||||
func _on_file_dialog_save_file(path: String) -> void:
|
func _on_file_dialog_save_file(path: String) -> void:
|
||||||
var deck: Deck = _deck_to_save.get_ref() as Deck
|
var deck: Deck = _deck_to_save.get_ref() as Deck
|
||||||
if !deck:
|
if !deck:
|
||||||
|
@ -82,7 +96,8 @@ func _on_file_dialog_save_file(path: String) -> void:
|
||||||
var f := FileAccess.open(path, FileAccess.WRITE)
|
var f := FileAccess.open(path, FileAccess.WRITE)
|
||||||
f.store_string(json)
|
f.store_string(json)
|
||||||
|
|
||||||
|
## Connected to [signal FileDialog.open_files] on [member file_dialog]. Opens
|
||||||
|
## the selected paths, instantiating [DeckRenderGraphEdit]s and [Deck]s for each.
|
||||||
func _on_file_dialog_open_files(paths: PackedStringArray) -> void:
|
func _on_file_dialog_open_files(paths: PackedStringArray) -> void:
|
||||||
for path in paths:
|
for path in paths:
|
||||||
var deck := DeckHolder.open_deck_from_file(path)
|
var deck := DeckHolder.open_deck_from_file(path)
|
||||||
|
@ -92,14 +107,14 @@ func _on_file_dialog_open_files(paths: PackedStringArray) -> void:
|
||||||
inst.initialize_from_deck()
|
inst.initialize_from_deck()
|
||||||
inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck))
|
inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck))
|
||||||
|
|
||||||
|
## Gets the currently active [Deck] from [member tab_container]
|
||||||
func get_active_deck() -> Deck:
|
func get_active_deck() -> Deck:
|
||||||
if tab_container.is_empty():
|
if tab_container.is_empty():
|
||||||
return null
|
return null
|
||||||
|
|
||||||
return (tab_container.get_content(tab_container.get_current_tab()) as DeckRendererGraphEdit).deck
|
return (tab_container.get_content(tab_container.get_current_tab()) as DeckRendererGraphEdit).deck
|
||||||
|
|
||||||
|
## Saves the active [Deck] in [member tab_container]
|
||||||
func save_active_deck() -> void:
|
func save_active_deck() -> void:
|
||||||
if get_active_deck().save_path.is_empty():
|
if get_active_deck().save_path.is_empty():
|
||||||
open_save_dialog("res://")
|
open_save_dialog("res://")
|
||||||
|
@ -108,7 +123,7 @@ func save_active_deck() -> void:
|
||||||
var f := FileAccess.open(get_active_deck().save_path, FileAccess.WRITE)
|
var f := FileAccess.open(get_active_deck().save_path, FileAccess.WRITE)
|
||||||
f.store_string(json)
|
f.store_string(json)
|
||||||
|
|
||||||
|
## Disconnects the [FileDialog] signals if they are already connected.
|
||||||
func disconnect_file_dialog_signals() -> void:
|
func disconnect_file_dialog_signals() -> void:
|
||||||
if file_dialog.file_selected.is_connected(_on_file_dialog_save_file):
|
if file_dialog.file_selected.is_connected(_on_file_dialog_save_file):
|
||||||
file_dialog.file_selected.disconnect(_on_file_dialog_save_file)
|
file_dialog.file_selected.disconnect(_on_file_dialog_save_file)
|
||||||
|
@ -116,7 +131,9 @@ func disconnect_file_dialog_signals() -> void:
|
||||||
if file_dialog.files_selected.is_connected(_on_file_dialog_open_files):
|
if file_dialog.files_selected.is_connected(_on_file_dialog_open_files):
|
||||||
file_dialog.files_selected.disconnect(_on_file_dialog_open_files)
|
file_dialog.files_selected.disconnect(_on_file_dialog_open_files)
|
||||||
|
|
||||||
|
## Connected to [signal DeckRenderGraphEdit.group_entered_request] to allow entering
|
||||||
|
## groups based off the given [param group_id] and [param deck]. As well as adding
|
||||||
|
## a corresponding tab to [member tab_container]
|
||||||
func _on_deck_renderer_group_enter_requested(group_id: String, deck: Deck) -> void:
|
func _on_deck_renderer_group_enter_requested(group_id: String, deck: Deck) -> void:
|
||||||
var group_deck := deck.get_group(group_id)
|
var group_deck := deck.get_group(group_id)
|
||||||
var deck_renderer: DeckRendererGraphEdit = DECK_SCENE.instantiate()
|
var deck_renderer: DeckRendererGraphEdit = DECK_SCENE.instantiate()
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
extends GraphNode
|
extends GraphNode
|
||||||
class_name DeckNodeRendererGraphNode
|
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
|
var node: DeckNode
|
||||||
|
|
||||||
|
## 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:
|
func _ready() -> void:
|
||||||
title = node.name
|
title = node.name
|
||||||
node.position_updated.connect(_on_node_position_updated)
|
node.position_updated.connect(_on_node_position_updated)
|
||||||
|
@ -50,17 +55,21 @@ func _ready() -> void:
|
||||||
Color.WHITE,
|
Color.WHITE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
## Connected to [signal GraphElement.position_offset_updated] and updates the
|
||||||
|
## [member node]s properties
|
||||||
func _on_position_offset_changed() -> void:
|
func _on_position_offset_changed() -> void:
|
||||||
node.position.x = position_offset.x
|
node.position.x = position_offset.x
|
||||||
node.position.y = position_offset.y
|
node.position.y = position_offset.y
|
||||||
|
|
||||||
|
## Connected to [member node]s [signal position_updated] to keep parity with the
|
||||||
|
## data position.
|
||||||
func _on_node_position_updated(new_position: Dictionary) -> void:
|
func _on_node_position_updated(new_position: Dictionary) -> void:
|
||||||
position_offset.x = new_position.x
|
position_offset.x = new_position.x
|
||||||
position_offset.y = new_position.y
|
position_offset.y = new_position.y
|
||||||
|
|
||||||
|
## 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:
|
func _on_node_port_added(port_idx: int) -> void:
|
||||||
var port := node.get_all_ports()[port_idx]
|
var port := node.get_all_ports()[port_idx]
|
||||||
|
|
||||||
|
@ -103,7 +112,8 @@ func _on_node_port_added(port_idx: int) -> void:
|
||||||
Color.WHITE,
|
Color.WHITE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
## 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:
|
func _on_node_port_removed(port_idx: int) -> void:
|
||||||
set_slot(
|
set_slot(
|
||||||
port_idx,
|
port_idx,
|
||||||
|
@ -117,7 +127,8 @@ func _on_node_port_removed(port_idx: int) -> void:
|
||||||
|
|
||||||
get_child(port_idx).queue_free()
|
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:
|
func _on_node_ports_updated() -> void:
|
||||||
clear_all_slots()
|
clear_all_slots()
|
||||||
for c in get_children():
|
for c in get_children():
|
||||||
|
|
|
@ -1,29 +1,40 @@
|
||||||
extends GraphEdit
|
extends GraphEdit
|
||||||
class_name DeckRendererGraphEdit
|
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")
|
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")
|
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
|
var search_popup_panel: PopupPanel
|
||||||
|
## Stores instance of [AddNodeMenu] that is used under [member search_popup_panel]
|
||||||
var add_node_menu: AddNodeMenu
|
var add_node_menu: AddNodeMenu
|
||||||
|
|
||||||
@export var add_node_popup_size: Vector2i = Vector2i(500, 300)
|
## 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
|
var popup_position: Vector2
|
||||||
|
|
||||||
|
## References the [Deck] that holds all the functional properties of this [DeckRendererGraphEdit]
|
||||||
var deck: Deck:
|
var deck: Deck:
|
||||||
set(v):
|
set(v):
|
||||||
deck = v
|
deck = v
|
||||||
deck.node_added.connect(_on_deck_node_added)
|
deck.node_added.connect(_on_deck_node_added)
|
||||||
deck.node_removed.connect(_on_deck_node_removed)
|
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)
|
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:
|
func _ready() -> void:
|
||||||
add_node_menu = ADD_NODE_MENU_SCENE.instantiate()
|
add_node_menu = ADD_NODE_MENU_SCENE.instantiate()
|
||||||
search_popup_panel = PopupPanel.new()
|
search_popup_panel = PopupPanel.new()
|
||||||
search_popup_panel.add_child(add_node_menu)
|
search_popup_panel.add_child(add_node_menu)
|
||||||
search_popup_panel.size = add_node_popup_size
|
search_popup_panel.size = search_popup_size
|
||||||
add_child(search_popup_panel, false, Node.INTERNAL_MODE_BACK)
|
add_child(search_popup_panel, false, Node.INTERNAL_MODE_BACK)
|
||||||
|
|
||||||
add_node_menu.node_selected.connect(_on_add_node_menu_node_selected)
|
add_node_menu.node_selected.connect(_on_add_node_menu_node_selected)
|
||||||
|
@ -31,7 +42,8 @@ func _ready() -> void:
|
||||||
connection_request.connect(attempt_connection)
|
connection_request.connect(attempt_connection)
|
||||||
disconnection_request.connect(attempt_disconnect)
|
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:
|
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 from_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(from_node_name))
|
||||||
var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node_name))
|
var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node_name))
|
||||||
|
@ -47,7 +59,8 @@ func attempt_connection(from_node_name: StringName, from_port: int, to_node_name
|
||||||
to_port
|
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:
|
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 from_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(from_node_name))
|
||||||
var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node_name))
|
var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node_name))
|
||||||
|
@ -64,7 +77,8 @@ func attempt_disconnect(from_node_name: StringName, from_port: int, to_node_name
|
||||||
to_port
|
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:
|
func get_node_renderer(node: DeckNode) -> DeckNodeRendererGraphNode:
|
||||||
for i: DeckNodeRendererGraphNode in get_children().slice(1):
|
for i: DeckNodeRendererGraphNode in get_children().slice(1):
|
||||||
if i.node == node:
|
if i.node == node:
|
||||||
|
@ -72,11 +86,11 @@ func get_node_renderer(node: DeckNode) -> DeckNodeRendererGraphNode:
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
## Updates [member Deck]s meta property "offset" whenever [member GraphEdit.scroll_offset]
|
||||||
func _on_scroll_offset_changed(offset: Vector2) -> void:
|
func _on_scroll_offset_changed(offset: Vector2) -> void:
|
||||||
deck.set_meta("offset", offset)
|
deck.set_meta("offset", offset)
|
||||||
|
|
||||||
|
## Setups all the data from the set [member deck] in this [DeckRendererGraphEdit]
|
||||||
func initialize_from_deck() -> void:
|
func initialize_from_deck() -> void:
|
||||||
# TODO: wait for https://github.com/godotengine/godot/issues/85005 to get merged
|
# 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
|
# until it is, all calls to GraphEdit#get_children will need to slice off the first element
|
||||||
|
@ -100,7 +114,8 @@ func initialize_from_deck() -> void:
|
||||||
|
|
||||||
refresh_connections()
|
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:
|
func refresh_connections() -> void:
|
||||||
for node_id in deck.nodes:
|
for node_id in deck.nodes:
|
||||||
var node: DeckNode = deck.nodes[node_id]
|
var node: DeckNode = deck.nodes[node_id]
|
||||||
|
@ -131,14 +146,16 @@ func refresh_connections() -> void:
|
||||||
to_node_port
|
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:
|
func _on_deck_node_added(node: DeckNode) -> void:
|
||||||
var inst: DeckNodeRendererGraphNode = NODE_SCENE.instantiate()
|
var inst: DeckNodeRendererGraphNode = NODE_SCENE.instantiate()
|
||||||
inst.node = node
|
inst.node = node
|
||||||
add_child(inst)
|
add_child(inst)
|
||||||
inst.position_offset = inst.node.position_as_vector2()
|
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:
|
func _on_deck_node_removed(node: DeckNode) -> void:
|
||||||
for renderer: DeckNodeRendererGraphNode in get_children().slice(1):
|
for renderer: DeckNodeRendererGraphNode in get_children().slice(1):
|
||||||
if renderer.node != node:
|
if renderer.node != node:
|
||||||
|
@ -147,14 +164,16 @@ func _on_deck_node_removed(node: DeckNode) -> void:
|
||||||
renderer.queue_free()
|
renderer.queue_free()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
## Utility function that gets all [DeckNodeRenderGraphNode]s that are selected
|
||||||
|
## See [member GraphNode.selected]
|
||||||
func get_selected_nodes() -> Array:
|
func get_selected_nodes() -> Array:
|
||||||
return get_children().slice(1).filter(
|
return get_children().slice(1).filter(
|
||||||
func(x: DeckNodeRendererGraphNode):
|
func(x: DeckNodeRendererGraphNode):
|
||||||
return x.selected
|
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:
|
func _gui_input(event: InputEvent) -> void:
|
||||||
if event.is_action_pressed("group_nodes") && get_selected_nodes().size() > 0:
|
if event.is_action_pressed("group_nodes") && get_selected_nodes().size() > 0:
|
||||||
print("?")
|
print("?")
|
||||||
|
@ -167,7 +186,8 @@ func _gui_input(event: InputEvent) -> void:
|
||||||
refresh_connections()
|
refresh_connections()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
## Handles entering groups with action "enter_group". Done here to bypass neighbor
|
||||||
|
## functionality.
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if !has_focus():
|
if !has_focus():
|
||||||
return
|
return
|
||||||
|
@ -179,7 +199,7 @@ func _input(event: InputEvent) -> void:
|
||||||
group_enter_requested.emit((get_selected_nodes()[0] as DeckNodeRendererGraphNode).node.group_id)
|
group_enter_requested.emit((get_selected_nodes()[0] as DeckNodeRendererGraphNode).node.group_id)
|
||||||
get_viewport().set_input_as_handled()
|
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:
|
func _on_popup_request(p_popup_position: Vector2) -> void:
|
||||||
var p := get_viewport_rect().position + get_global_mouse_position()
|
var p := get_viewport_rect().position + get_global_mouse_position()
|
||||||
p += Vector2(10, 10)
|
p += Vector2(10, 10)
|
||||||
|
@ -188,7 +208,9 @@ func _on_popup_request(p_popup_position: Vector2) -> void:
|
||||||
add_node_menu.focus_search_bar()
|
add_node_menu.focus_search_bar()
|
||||||
popup_position = p_popup_position
|
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:
|
func _on_add_node_menu_node_selected(type: String) -> void:
|
||||||
var node := NodeDB.instance_node(type) as DeckNode
|
var node := NodeDB.instance_node(type) as DeckNode
|
||||||
deck.add_node_inst(node)
|
deck.add_node_inst(node)
|
||||||
|
|
|
@ -1,17 +1,32 @@
|
||||||
extends VBoxContainer
|
extends VBoxContainer
|
||||||
class_name TabContainerCustom
|
class_name TabContainerCustom
|
||||||
|
|
||||||
|
## Custom Recreation of [TabContainer] for Flexibility
|
||||||
|
##
|
||||||
|
## Allows for more customizability within the [TabBar] thats used mainly. Extra buttons etc.
|
||||||
|
|
||||||
|
## Reference to the [TabBar] at the top of the Container.
|
||||||
@onready var tab_bar: TabBar = %TabBar
|
@onready var tab_bar: TabBar = %TabBar
|
||||||
|
## Reference to the [Button] at the end of the [TabBar] that's
|
||||||
|
## used for adding new Tabs
|
||||||
@onready var add_tab_button: Button = %Button
|
@onready var add_tab_button: Button = %Button
|
||||||
|
## Reference to the [MarginContainer] around the Tabs Contents.
|
||||||
@onready var content_container: MarginContainer = %ContentContainer
|
@onready var content_container: MarginContainer = %ContentContainer
|
||||||
|
|
||||||
|
## Emitted when the add [Button] within [member tab_bar] is pressed
|
||||||
signal add_button_pressed
|
signal add_button_pressed
|
||||||
|
## Emitted when the current tab in [member tab_bar] is changed.
|
||||||
signal tab_changed(tab: int)
|
signal tab_changed(tab: int)
|
||||||
|
## Emitted when a tab in [member tab_bar] has been closed.
|
||||||
|
## See [signal TabBar.tab_close_requested]
|
||||||
signal tab_closed(tab: int)
|
signal tab_closed(tab: int)
|
||||||
|
## Emitted when a request to close a tab in the [member tab_bar] has been
|
||||||
|
## requested using [signal TabBar.tab_close_pressed]
|
||||||
signal tab_close_requested(tab: int)
|
signal tab_close_requested(tab: int)
|
||||||
|
## Emitted when the order of the tabs in [member tab_bar] has been changed.
|
||||||
signal tab_rearranged(old: int, new: int)
|
signal tab_rearranged(old: int, new: int)
|
||||||
|
|
||||||
|
## Holds the previously active tab in [member tab_bar]
|
||||||
var _previous_active_tab: int = -1
|
var _previous_active_tab: int = -1
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,21 +58,21 @@ func _ready() -> void:
|
||||||
_previous_active_tab = idx_to
|
_previous_active_tab = idx_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
## Adds the given [Node] as "content" for the given tabs name as a [String]
|
||||||
func add_content(c: Node, tab_title: String) -> void:
|
func add_content(c: Node, tab_title: String) -> void:
|
||||||
tab_bar.add_tab(tab_title)
|
tab_bar.add_tab(tab_title)
|
||||||
content_container.add_child(c)
|
content_container.add_child(c)
|
||||||
tab_bar.set_current_tab(tab_bar.tab_count - 1)
|
tab_bar.set_current_tab(tab_bar.tab_count - 1)
|
||||||
|
|
||||||
|
## Returns the count of tabs in [member tab_bar]
|
||||||
func get_tab_count() -> int:
|
func get_tab_count() -> int:
|
||||||
return tab_bar.tab_count
|
return tab_bar.tab_count
|
||||||
|
|
||||||
|
## Returns [code]true[/code] if [method get_tab_count] returns 0.
|
||||||
func is_empty() -> bool:
|
func is_empty() -> bool:
|
||||||
return get_tab_count() == 0
|
return get_tab_count() == 0
|
||||||
|
|
||||||
|
## Closes the tab that is at the given [param tab] in [member tab_bar]
|
||||||
func close_tab(tab: int) -> void:
|
func close_tab(tab: int) -> void:
|
||||||
content_container.get_child(tab).queue_free()
|
content_container.get_child(tab).queue_free()
|
||||||
if !tab_bar.select_previous_available():
|
if !tab_bar.select_previous_available():
|
||||||
|
@ -67,18 +82,20 @@ func close_tab(tab: int) -> void:
|
||||||
if tab_bar.tab_count == 0:
|
if tab_bar.tab_count == 0:
|
||||||
_previous_active_tab = -1
|
_previous_active_tab = -1
|
||||||
|
|
||||||
|
## Returns the currently selected tab in [member tab_bar]
|
||||||
func get_current_tab() -> int:
|
func get_current_tab() -> int:
|
||||||
return tab_bar.current_tab
|
return tab_bar.current_tab
|
||||||
|
|
||||||
|
## Returns the child of [member content_container] at the given [param idx]
|
||||||
func get_content(idx: int) -> Control:
|
func get_content(idx: int) -> Control:
|
||||||
return content_container.get_child(idx)
|
return content_container.get_child(idx)
|
||||||
|
|
||||||
|
## Sets the metadata value for the tab at index [param tab_idx], which can be
|
||||||
|
## retrieved later using [method TabBar.get_tab_metadata()]
|
||||||
func set_tab_metadata(tab: int, metadata: Variant) -> void:
|
func set_tab_metadata(tab: int, metadata: Variant) -> void:
|
||||||
tab_bar.set_tab_metadata(tab, metadata)
|
tab_bar.set_tab_metadata(tab, metadata)
|
||||||
|
|
||||||
|
## Returns the metadata value set to the tab at index [param tab_idx] using set_tab_metadata().
|
||||||
|
## If no metadata was previously set, returns null by default.
|
||||||
func get_tab_metadata(tab: int) -> Variant:
|
func get_tab_metadata(tab: int) -> Variant:
|
||||||
return tab_bar.get_tab_metadata(tab)
|
return tab_bar.get_tab_metadata(tab)
|
||||||
|
|
Loading…
Reference in a new issue