yag's doc sprint (#4)

documented everything (except NodeDB) in the classes/ folder

Reviewed-on: https://codeberg.org/Eroax/Re-DotDeck/pulls/4
Co-authored-by: Lera Elvoé <yagich@poto.cafe>
Co-committed-by: Lera Elvoé <yagich@poto.cafe>
This commit is contained in:
Lera Elvoé 2023-11-25 10:40:53 +00:00 committed by Eroax
parent 2264890113
commit b36bdaf71c
7 changed files with 207 additions and 11 deletions

View file

@ -1,7 +1,13 @@
class_name Deck class_name Deck
## A deck/graph with nodes.
##
## A container for [DeckNode]s, managing connections between them.
## The [DeckNode]s that belong to this deck. The key is the node's id, the value
## is the [DeckNode] instance.
var nodes: Dictionary var nodes: Dictionary
## The list of [DeckType] for easy port creation.
enum Types{ enum Types{
ERROR = -1, ERROR = -1,
BOOL, BOOL,
@ -11,7 +17,7 @@ enum Types{
DICTIONARY, DICTIONARY,
} }
## A dictionary mapping [enum Types] to [DeckType] subclasses.
static var type_assoc: Dictionary = { static var type_assoc: Dictionary = {
Types.ERROR: DeckType.DeckTypeError, Types.ERROR: DeckType.DeckTypeError,
Types.BOOL: DeckType.DeckTypeBool, Types.BOOL: DeckType.DeckTypeBool,
@ -21,27 +27,47 @@ static var type_assoc: Dictionary = {
Types.DICTIONARY: DeckType.DeckTypeDictionary, Types.DICTIONARY: DeckType.DeckTypeDictionary,
} }
## A map of variables set on this deck.
var variable_stack: Dictionary = {} var variable_stack: Dictionary = {}
## The path to save this deck on the file system.
var save_path: String = "" var save_path: String = ""
var is_group: bool = false var is_group: bool = false
var groups: Dictionary = {} #Dictionary[String -> Deck.id, Deck] ## List of groups belonging to this deck, in the format of[br]
## [code]Dictionary[String -> Deck.id, Deck][/code]
var groups: Dictionary = {}
## A unique identifier for this deck.
var id: String = "" var id: String = ""
## The parent deck of this deck, if this is a group.
var _belonging_to: Deck # for groups var _belonging_to: Deck # for groups
## The ID of this group's input node. Used only if [member is_group] is [code]true[/code].
var group_input_node: String var group_input_node: String
## The ID of this group's input node. Used only if [member is_group] is [code]true[/code].
var group_output_node: String var group_output_node: String
## The ID of the group node this group is represented by, contained in this deck's parent deck.
## Used only if [member is_group] is [code]true[/code].
## @experimental
var group_node: String var group_node: String
## Emitted when a node has been added to this deck.
signal node_added(node: DeckNode) signal node_added(node: DeckNode)
## Emitted when a node has been removed from this deck.
signal node_removed(node: DeckNode) signal node_removed(node: DeckNode)
## Instantiate a node by its' [member DeckNode.node_type] and add it to this deck.[br]
## See [method add_node_inst] for parameter descriptions.
func add_node_type(type: String, assign_id: String = "", assign_to_self: bool = true) -> DeckNode: func add_node_type(type: String, assign_id: String = "", assign_to_self: bool = true) -> DeckNode:
var node_inst: DeckNode = NodeDB.instance_node(type) var node_inst: DeckNode = NodeDB.instance_node(type)
return add_node_inst(node_inst, assign_id, assign_to_self) return add_node_inst(node_inst, assign_id, assign_to_self)
## Add a [DeckNode] instance to this deck.[br]
## If [param assign_id] is empty, the node will get its' ID (re-)assigned.
## Otherwise, it will be assigned to be that value.[br]
## If [param assign_to_self] is [code]true[/code], the node's
## [member DeckNode._belonging_to] property will be set to [code]self[/code].
func add_node_inst(node: DeckNode, assign_id: String = "", assign_to_self: bool = true) -> DeckNode: func add_node_inst(node: DeckNode, assign_id: String = "", assign_to_self: bool = true) -> DeckNode:
if assign_to_self: if assign_to_self:
node._belonging_to = self node._belonging_to = self
@ -58,10 +84,12 @@ func add_node_inst(node: DeckNode, assign_id: String = "", assign_to_self: bool
return node return node
## Get a node belonging to this deck by its' ID.
func get_node(uuid: String) -> DeckNode: func get_node(uuid: String) -> DeckNode:
return nodes.get(uuid) return nodes.get(uuid)
## Attempt to connect two nodes. Returns [code]true[/code] if the connection succeeded.
func connect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int, to_input_port: int) -> bool: func connect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int, to_input_port: int) -> bool:
# first, check that we can do the type conversion. # first, check that we can do the type conversion.
var type_a: Types = from_node.get_output_ports()[from_output_port].type var type_a: Types = from_node.get_output_ports()[from_output_port].type
@ -77,6 +105,7 @@ func connect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int
return true return true
## Remove a connection from two nodes.
func disconnect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int, to_input_port: int) -> void: func disconnect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int, to_input_port: int) -> void:
var hash = {to_node._id: to_input_port}.hash() var hash = {to_node._id: to_input_port}.hash()
@ -84,10 +113,12 @@ func disconnect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port:
to_node.remove_incoming_connection(to_input_port) to_node.remove_incoming_connection(to_input_port)
## Returns true if this deck has no nodes and no variables.
func is_empty() -> bool: func is_empty() -> bool:
return nodes.is_empty() && variable_stack.is_empty() return nodes.is_empty() && variable_stack.is_empty()
## Remove a node from this deck.
func remove_node(uuid: String) -> void: func remove_node(uuid: String) -> void:
var node = nodes.get(uuid) var node = nodes.get(uuid)
nodes.erase(uuid) nodes.erase(uuid)
@ -95,6 +126,9 @@ func remove_node(uuid: String) -> void:
node_removed.emit(node) node_removed.emit(node)
## Group the [param nodes_to_group] into a new deck and return it.
## Returns [code]null[/code] on failure.[br]
## Adds a group node to this deck, and adds group input and output nodes in the group.
func group_nodes(nodes_to_group: Array) -> Deck: func group_nodes(nodes_to_group: Array) -> Deck:
if nodes_to_group.is_empty(): if nodes_to_group.is_empty():
return null return null
@ -151,10 +185,12 @@ func group_nodes(nodes_to_group: Array) -> Deck:
return group return group
## Get a group belonging to this deck by its ID.
func get_group(uuid: String) -> Deck: func get_group(uuid: String) -> Deck:
return groups.get(uuid) return groups.get(uuid)
## Returns a [Dictionary] representation of this deck.
func to_dict(with_meta: bool = true) -> Dictionary: func to_dict(with_meta: bool = true) -> Dictionary:
var inner := { var inner := {
"nodes": {}, "nodes": {},
@ -183,6 +219,7 @@ func to_dict(with_meta: bool = true) -> Dictionary:
return d return d
## Create a new deck from a [Dictionary] representation, such as one created by [method to_dict].
static func from_dict(data: Dictionary, path: String = "") -> Deck: static func from_dict(data: Dictionary, path: String = "") -> Deck:
var deck := Deck.new() var deck := Deck.new()
deck.save_path = path deck.save_path = path

View file

@ -1,9 +1,12 @@
class_name DeckHolder class_name DeckHolder
## @experimental
## A static class holding references to all decks opened in the current session.
## List of decks opened this session.
static var decks: Array[Deck] static var decks: Array[Deck]
## Returns a new empty deck and assigns a new random ID to it.
static func add_empty_deck() -> Deck: static func add_empty_deck() -> Deck:
var deck := Deck.new() var deck := Deck.new()
DeckHolder.decks.append(deck) DeckHolder.decks.append(deck)
@ -12,6 +15,7 @@ static func add_empty_deck() -> Deck:
return deck return deck
## Opens a deck from the [param path].
static func open_deck_from_file(path: String) -> Deck: static func open_deck_from_file(path: String) -> Deck:
var f := FileAccess.open(path, FileAccess.READ) var f := FileAccess.open(path, FileAccess.READ)
if f.get_error() != OK: if f.get_error() != OK:
@ -23,5 +27,6 @@ static func open_deck_from_file(path: String) -> Deck:
return deck return deck
## Unloads a deck.
static func close_deck(deck: Deck) -> void: static func close_deck(deck: Deck) -> void:
DeckHolder.decks.erase(deck) DeckHolder.decks.erase(deck)

View file

@ -1,25 +1,44 @@
class_name DeckNode class_name DeckNode
## A node in a [Deck].
##
## Nodes are the essential building block of a [Deck] graph. They can have connections between ports
## and send data through them.
## The name initially shown to a renderer.
var name: String var name: String
## [code]Dictionary[int -> output port, Array[Dictionary[String -> DeckNode#_id, int -> input port]]] ## A map of outgoing connections from this node, in the format[br]
## [code]Dictionary[int -> output port, Array[Dictionary[String -> DeckNode#_id, int -> input port]]][/code]
var outgoing_connections: Dictionary var outgoing_connections: Dictionary
## [code]Dictionary[int -> input port, [Dictionary[String -> DeckNode#_id, int -> output port]] ## A map of incoming connections to this node, in the format[br]
## [code]Dictionary[int -> input port, [Dictionary[String -> DeckNode#_id, int -> output port]][/code]
var incoming_connections: Dictionary var incoming_connections: Dictionary
## A list of [Port]s on this node.
var ports: Array[Port] var ports: Array[Port]
## The deck this node belongs to.
var _belonging_to: Deck var _belonging_to: Deck
## A unique identifier for this node.
var _id: String var _id: String
## The type of this node, used for instantiation.
var node_type: String var node_type: String
## The description of this node, shown to the user by a renderer.
var description: String var description: String
## A list of aliases for this node, used by search.
var aliases: Array[String] var aliases: Array[String]
## The category of this node. Must be snake_case. This is additional data which
## a renderer can optionally show to the user.
var category: String var category: String
## Controls whether this node should appear in [SearchProvider].
var appears_in_search: bool = true var appears_in_search: bool = true
## A list of additional properties to save when this node is saved.
var props_to_serialize: Array[StringName] var props_to_serialize: Array[StringName]
## The position of this node relative to the parent graph.
## Only used by renderers.
var position: Dictionary = {"x": 0.0, "y": 0.0} var position: Dictionary = {"x": 0.0, "y": 0.0}
enum PortType{ enum PortType{
@ -28,28 +47,40 @@ enum PortType{
VIRTUAL, ## Virtual port type (no slot on left [i]or[/i] right). VIRTUAL, ## Virtual port type (no slot on left [i]or[/i] right).
} }
## Emitted when this node has been moved.
signal position_updated(new_position: Dictionary) signal position_updated(new_position: Dictionary)
## Emitted when a port has been added.
signal port_added(port: int) signal port_added(port: int)
## Emitted when a port has been removed.
signal port_removed(port: int) signal port_removed(port: int)
## Emitted when a port or multiple ports have been updated (added or removed).
signal ports_updated() signal ports_updated()
## Emitted when a connection from this node has been added.
signal outgoing_connection_added(from_port: int) signal outgoing_connection_added(from_port: int)
## Emitted when a connection from this node has been removed.
signal outgoing_connection_removed(from_port: int) signal outgoing_connection_removed(from_port: int)
## Emitted when a connection to this node has been added.
signal incoming_connection_added(from_port: int) signal incoming_connection_added(from_port: int)
## Emitted when a connection to this node has been removed.
signal incoming_connection_removed(from_port: int) signal incoming_connection_removed(from_port: int)
## Add an input port to this node. Usually only used at initialization.
func add_input_port(type: Deck.Types, label: String, descriptor: String = "") -> void: func add_input_port(type: Deck.Types, label: String, descriptor: String = "") -> void:
add_port(type, label, PortType.INPUT, get_input_ports().size(), descriptor) add_port(type, label, PortType.INPUT, get_input_ports().size(), descriptor)
## Add an output port to this node. Usually only used at initialization.
func add_output_port(type: Deck.Types, label: String, descriptor: String = "") -> void: func add_output_port(type: Deck.Types, label: String, descriptor: String = "") -> void:
add_port(type, label, PortType.OUTPUT, get_output_ports().size(), descriptor) add_port(type, label, PortType.OUTPUT, get_output_ports().size(), descriptor)
## Add a virtual port to this node. Usually only used at initialization.
func add_virtual_port(type: Deck.Types, label: String, descriptor: String = "") -> void: func add_virtual_port(type: Deck.Types, label: String, descriptor: String = "") -> void:
add_port(type, label, PortType.VIRTUAL, get_virtual_ports().size(), descriptor) add_port(type, label, PortType.VIRTUAL, get_virtual_ports().size(), descriptor)
## Add a port to this node. Usually only used at initialization.
func add_port(type: Deck.Types, label: String, port_type: PortType, index_of_type: int, descriptor: String = "") -> void: func add_port(type: Deck.Types, label: String, port_type: PortType, index_of_type: int, descriptor: String = "") -> void:
var port := Port.new(type, label, ports.size(), port_type, index_of_type, descriptor) var port := Port.new(type, label, ports.size(), port_type, index_of_type, descriptor)
ports.append(port) ports.append(port)
@ -57,6 +88,7 @@ func add_port(type: Deck.Types, label: String, port_type: PortType, index_of_typ
ports_updated.emit() ports_updated.emit()
## Remove a port from this node.
func remove_port(port_idx: int) -> void: func remove_port(port_idx: int) -> void:
outgoing_connections.erase(port_idx) outgoing_connections.erase(port_idx)
incoming_connections.erase(port_idx) incoming_connections.erase(port_idx)
@ -64,6 +96,7 @@ func remove_port(port_idx: int) -> void:
port_removed.emit(port_idx) port_removed.emit(port_idx)
## Send data to all outgoing connections on port [param from_output_port].
func send(from_output_port: int, data: DeckType, extra_data: Array = []) -> void: func send(from_output_port: int, data: DeckType, extra_data: Array = []) -> void:
if outgoing_connections.get(from_output_port) == null: if outgoing_connections.get(from_output_port) == null:
return return
@ -76,10 +109,12 @@ func send(from_output_port: int, data: DeckType, extra_data: Array = []) -> void
get_node(node)._receive(connection[node], data, extra_data) get_node(node)._receive(connection[node], data, extra_data)
## Virtual function that's called when this node receives data from another node's [method send] call.
func _receive(to_input_port: int, data: DeckType, extra_data: Array = []) -> void: func _receive(to_input_port: int, data: DeckType, extra_data: Array = []) -> void:
pass pass
## @deprecated
func add_outgoing_connection_to_port(output_port: int, to_node: String, to_input_port: int) -> void: func add_outgoing_connection_to_port(output_port: int, to_node: String, to_input_port: int) -> void:
add_outgoing_connection( add_outgoing_connection(
get_global_port_idx_from_output(output_port), get_global_port_idx_from_output(output_port),
@ -88,6 +123,8 @@ func add_outgoing_connection_to_port(output_port: int, to_node: String, to_input
) )
## Add a connection from the output port at [param from_port] to [param to_node]'s input port
## at [param to_port].
func add_outgoing_connection(from_port: int, to_node: String, to_port: int) -> void: func add_outgoing_connection(from_port: int, to_node: String, to_port: int) -> void:
var port_connections: Array = outgoing_connections.get(from_port, []) var port_connections: Array = outgoing_connections.get(from_port, [])
port_connections.append({to_node: to_port}) port_connections.append({to_node: to_port})
@ -96,12 +133,17 @@ func add_outgoing_connection(from_port: int, to_node: String, to_port: int) -> v
outgoing_connection_added.emit(from_port) outgoing_connection_added.emit(from_port)
## Add an incoming connection from [param from_node]'s output port at [param from_port] to this node's
## input port at [param to_port].
func add_incoming_connection(to_port: int, from_node: String, from_port: int) -> void: func add_incoming_connection(to_port: int, from_node: String, from_port: int) -> void:
var connection := {from_node: from_port} var connection := {from_node: from_port}
incoming_connections[to_port] = connection incoming_connections[to_port] = connection
incoming_connection_added.emit(to_port) incoming_connection_added.emit(to_port)
## Request a value from an incoming connection on this node's input port at [param on_port].
## Returns [code]null[/code] if no incoming connection exists on that port.
## The connected node may also return [code]null[/code].
func request_value(on_port: int) -> Variant: func request_value(on_port: int) -> Variant:
if !incoming_connections.has(on_port): if !incoming_connections.has(on_port):
return null return null
@ -111,11 +153,14 @@ func request_value(on_port: int) -> Variant:
return node._value_request(connection.values()[0]) return node._value_request(connection.values()[0])
# override this ## Virtual function that's called when this node has been requested a value from the output port
## at [param from_port].
func _value_request(from_port: int) -> Variant: func _value_request(from_port: int) -> Variant:
return null return null
## Remove an outgoing connection from this node.
## Does [b]not[/b] remove the other node's incoming connection equivalent.
func remove_outgoing_connection(from_port: int, connection_hash: int) -> void: func remove_outgoing_connection(from_port: int, connection_hash: int) -> void:
var port_connections: Array = (outgoing_connections.get(from_port, []) as Array).duplicate(true) var port_connections: Array = (outgoing_connections.get(from_port, []) as Array).duplicate(true)
if port_connections.is_empty(): if port_connections.is_empty():
@ -138,15 +183,19 @@ func remove_outgoing_connection(from_port: int, connection_hash: int) -> void:
outgoing_connection_removed.emit(from_port) outgoing_connection_removed.emit(from_port)
## Remove an incoming connection to this node on the input port at [param to_port].
## Does [b]not[/b] remove the other node's outgoing connection equivalent.
func remove_incoming_connection(to_port: int) -> void: func remove_incoming_connection(to_port: int) -> void:
incoming_connections.erase(to_port) incoming_connections.erase(to_port)
incoming_connection_removed.emit(to_port) incoming_connection_removed.emit(to_port)
## @deprecated
func remove_outgoing_connection_from_port(output_port: int, connection_hash: int) -> void: func remove_outgoing_connection_from_port(output_port: int, connection_hash: int) -> void:
remove_outgoing_connection(get_global_port_idx_from_output(output_port), connection_hash) remove_outgoing_connection(get_global_port_idx_from_output(output_port), connection_hash)
## Returns a list of all input ports.
func get_input_ports() -> Array[Port]: func get_input_ports() -> Array[Port]:
return ports.filter( return ports.filter(
func(port: Port) -> bool: func(port: Port) -> bool:
@ -154,6 +203,7 @@ func get_input_ports() -> Array[Port]:
) )
## Returns a list of all output ports.
func get_output_ports() -> Array[Port]: func get_output_ports() -> Array[Port]:
return ports.filter( return ports.filter(
func(port: Port) -> bool: func(port: Port) -> bool:
@ -161,6 +211,7 @@ func get_output_ports() -> Array[Port]:
) )
## Returns a list of all virtual ports.
func get_virtual_ports() -> Array[Port]: func get_virtual_ports() -> Array[Port]:
return ports.filter( return ports.filter(
func(port: Port) -> bool: func(port: Port) -> bool:
@ -168,6 +219,7 @@ func get_virtual_ports() -> Array[Port]:
) )
## Returns the global port index from the input port index at [param idx].
func get_global_port_idx_from_input(idx: int) -> int: func get_global_port_idx_from_input(idx: int) -> int:
if get_input_ports().size() > idx: if get_input_ports().size() > idx:
return get_input_ports()[idx].index return get_input_ports()[idx].index
@ -175,6 +227,7 @@ func get_global_port_idx_from_input(idx: int) -> int:
return -1 return -1
## Returns the global port index from the output port index at [param idx].
func get_global_port_idx_from_output(idx: int) -> int: func get_global_port_idx_from_output(idx: int) -> int:
if get_output_ports().size() > idx: if get_output_ports().size() > idx:
return get_output_ports()[idx].index return get_output_ports()[idx].index
@ -182,6 +235,7 @@ func get_global_port_idx_from_output(idx: int) -> int:
return -1 return -1
## Returns the global port index from the virtual port index at [param idx].
func get_global_port_idx_from_virtual(idx: int) -> int: func get_global_port_idx_from_virtual(idx: int) -> int:
if get_virtual_ports().size() > idx: if get_virtual_ports().size() > idx:
return get_virtual_ports()[idx].index return get_virtual_ports()[idx].index
@ -189,44 +243,52 @@ func get_global_port_idx_from_virtual(idx: int) -> int:
return -1 return -1
## Get a port's local index from the global port index at [param idx].
func get_port_type_idx_from_global(idx: int) -> int: func get_port_type_idx_from_global(idx: int) -> int:
return ports[idx].index_of_type return ports[idx].index_of_type
## Returns the amount of outgoing connections on output port at [param port].
func get_outgoing_connection_count(port: int) -> int: func get_outgoing_connection_count(port: int) -> int:
return (outgoing_connections[port] as Array).size() return (outgoing_connections[port] as Array).size()
## @deprecated
func get_outgoing_connection_count_on_output(port: int) -> int: func get_outgoing_connection_count_on_output(port: int) -> int:
return get_outgoing_connection_count(get_global_port_idx_from_output(port)) return get_outgoing_connection_count(get_global_port_idx_from_output(port))
## Returns [code]true[/code] if the node has an incoming connection in input port at [param port].
func has_incoming_connection(port: int) -> bool: func has_incoming_connection(port: int) -> bool:
return incoming_connections.has(port) return incoming_connections.has(port)
## @deprecated
func has_incoming_connection_on_input(port: int) -> bool: func has_incoming_connection_on_input(port: int) -> bool:
return has_incoming_connection(get_global_port_idx_from_input(port)) return has_incoming_connection(get_global_port_idx_from_input(port))
## Returns the list of all ports.
func get_all_ports() -> Array[Port]: func get_all_ports() -> Array[Port]:
return ports return ports
## Get a sibling node by its' ID.
func get_node(uuid: String) -> DeckNode: func get_node(uuid: String) -> DeckNode:
return _belonging_to.get_node(uuid) return _belonging_to.get_node(uuid)
# override this to do setup before connections are loaded but after props were set ## Virtual function that's called during deserialization before connections are loaded in.
func _pre_connection() -> void: func _pre_connection() -> void:
pass pass
# override this to do extra setup after it's done loading from dictionary ## Virtual function that's called after the node has been deserialized.
func _post_load() -> void: func _post_load() -> void:
pass pass
## Returns a [Dictionary] representation of this node.
func to_dict(with_meta: bool = true) -> Dictionary: func to_dict(with_meta: bool = true) -> Dictionary:
var d := { var d := {
"_id": _id, "_id": _id,
@ -254,5 +316,6 @@ func to_dict(with_meta: bool = true) -> Dictionary:
return d return d
## Returns the node's [member position] as a [Vector2].
func position_as_vector2() -> Vector2: func position_as_vector2() -> Vector2:
return Vector2(position.x, position.y) return Vector2(position.x, position.y)

View file

@ -1,14 +1,30 @@
class_name Port class_name Port
## A data type representing a port of a [DeckNode].
##
## Ports are used for connections between [DeckNode]s and can contain data that is passed between
## them on a node.
## The type index of this port.
var type: Deck.Types var type: Deck.Types
## The label of this port. Used by the renderer to display. How it's displayed depends on the renderer
## and the [member descriptor].
var label: String var label: String
## Hints to the renderer on how to display this port.[br]
## Can be either one of these: [code]button textfield spinbox slider textblock codeblock checkbox singlechoice multichoice[/code].[br]
## Additional descriptor properties can be specified after the type, delimited by a colon ([code]:[/code]).[br]
## @experimental
var descriptor: String var descriptor: String
## A callback to get this port's value. Intended usage is by renderers that show an input field of some kind.
var value_callback: Callable var value_callback: Callable
## The type of this port (input, output or virtual)
var port_type: DeckNode.PortType var port_type: DeckNode.PortType
## The local index of this port.
var index_of_type: int var index_of_type: int
## The global index of this port.
var index: int var index: int
## The value of this port.
var value: Variant: set = set_value var value: Variant: set = set_value

View file

@ -1,5 +1,10 @@
class_name SearchProvider class_name SearchProvider
## A class facilitating the searching of nodes.
##
## Allows a renderer or subscribing client to search for nodes registered in NodeDB, optionally with filters.
## A list of all filters that can be applied to a search string.
static var filters: Array[Filter] = [ static var filters: Array[Filter] = [
# favorites filter. will only show nodes marked as favorite. syntax: "#f" # favorites filter. will only show nodes marked as favorite. syntax: "#f"
Filter.new( Filter.new(
@ -40,6 +45,7 @@ static var filters: Array[Filter] = [
] ]
## Performs a search for nodes. Filters can be provided directly in the search [param term].
static func search(term: String) -> Array[NodeDB.NodeDescriptor]: static func search(term: String) -> Array[NodeDB.NodeDescriptor]:
var res: Array[NodeDB.NodeDescriptor] = [] var res: Array[NodeDB.NodeDescriptor] = []
@ -75,6 +81,10 @@ static func search(term: String) -> Array[NodeDB.NodeDescriptor]:
return filtered_res return filtered_res
## A filter that can be applied to a search term in [SearchProvider].
##
## Filters have a set of functions that are run by the [SearchProvider]
## to determine if any items should be removed from the final match list.
class Filter: class Filter:
## Return [code]true[/code] if this filter should be applied to the search.[br] ## Return [code]true[/code] if this filter should be applied to the search.[br]
## [code]Callable(search_string: String) -> bool[/code] ## [code]Callable(search_string: String) -> bool[/code]

View file

@ -1,9 +1,11 @@
class_name DeckType class_name DeckType
## Base class for defining the types that can be used on a [Port].
var _value: Variant var _value: Variant
var _success: bool = true var _success: bool = true
## Returns [code]true[/code] if the type is valid and can be used.
func is_valid() -> bool: func is_valid() -> bool:
return _success return _success
@ -16,11 +18,17 @@ func set_value(new_value: Variant) -> void:
_value = new_value _value = new_value
## Virtual function. Used to convert [param other] to the overriding class' type.
static func from(other: DeckType): static func from(other: DeckType):
return null return null
## Generic Error type.
##
## Always returns [code]null[/code] as the value, and [code]false[/code] as success.[br]
## Always returns a new [DeckType.DeckTypeError] on conversion from any other type.
class DeckTypeError extends DeckType: class DeckTypeError extends DeckType:
## An optional error message.
var error_message: String var error_message: String
@ -35,11 +43,16 @@ class DeckTypeError extends DeckType:
return DeckTypeError.new() return DeckTypeError.new()
## Numeric type. Corresponds to the JSON number type, so only supports the float primitive type.
class DeckTypeNumeric extends DeckType: class DeckTypeNumeric extends DeckType:
func _init(value: float = 0.0) -> void: func _init(value: float = 0.0) -> void:
_value = value _value = value
## Converts either a [DeckType.DeckTypeString] or [DeckType.DeckTypeBool] to numeric.
## In the case of [DeckType.DeckTypeBool], [code]1.0[/code] is used for [code]true[/code],
## [code]0.0[/code] for [code]false[/code].[br]
## In the case of [DeckType.DeckTypeString], converts to String if it is a valid number
## or [DeckType.DeckTypeError] otherwise.
static func from(other: DeckType): static func from(other: DeckType):
if other is DeckTypeNumeric: if other is DeckTypeNumeric:
return other return other
@ -65,6 +78,7 @@ class DeckTypeNumeric extends DeckType:
return err return err
## String type. Corresponds to the JSON string type. Any type can be converted to String.
class DeckTypeString extends DeckType: class DeckTypeString extends DeckType:
func _init(value: String = "") -> void: func _init(value: String = "") -> void:
_value = value _value = value
@ -78,11 +92,17 @@ class DeckTypeString extends DeckType:
inst._value = var_to_str(other.get_value()) inst._value = var_to_str(other.get_value())
## Boolean type. Corresponds to the JSON bool type.
class DeckTypeBool extends DeckType: class DeckTypeBool extends DeckType:
func _init(value: bool = false) -> void: func _init(value: bool = false) -> void:
_value = value _value = value
## Converts either [DeckType.DeckTypeNumeric], [DeckType.DeckTypeDictionary] or [DeckType.DeckTypeArray]
## to a boolean.[br]
## In the case of [DeckType.DeckTypeNumeric], the resulting value will be [code]false[/code]
## if the value is a zero value ([code]0.0[/code] and [code]-0.0[/code]), [code]true[/code] otherwise.[br]
## In the case of [DeckType.DeckTypeDictionary] or [DeckType.DeckTypeArray],
## the resulting value will be [code]true[/code] if the container is not empty.
static func from(other: DeckType): static func from(other: DeckType):
if other is DeckTypeBool: if other is DeckTypeBool:
return other return other
@ -98,11 +118,14 @@ class DeckTypeBool extends DeckType:
return inst return inst
## Array type. Corresponds to the JSON Array type.
class DeckTypeArray extends DeckType: class DeckTypeArray extends DeckType:
func _init(value: Array = []) -> void: func _init(value: Array = []) -> void:
_value = value _value = value
## Arrays can only be converted from a string in the format
## [code]"["foo", 2, "bar"][/code].
static func from(other: DeckType): static func from(other: DeckType):
if other is DeckTypeString: if other is DeckTypeString:
var inst := DeckTypeArray.new() var inst := DeckTypeArray.new()
@ -114,11 +137,14 @@ class DeckTypeArray extends DeckType:
return err return err
## Dictionary class. Corresponds to the JSON Object type.
class DeckTypeDictionary extends DeckType: class DeckTypeDictionary extends DeckType:
func _init(value: Dictionary = {}) -> void: func _init(value: Dictionary = {}) -> void:
_value = value _value = value
## Dictionaries can only be converted from a string in the format
## [code]{"key": "value"}[/code].
static func from(other: DeckType): static func from(other: DeckType):
if other is DeckTypeString: if other is DeckTypeString:
var inst := DeckTypeDictionary.new() var inst := DeckTypeDictionary.new()

View file

@ -1,13 +1,18 @@
extends MarginContainer extends MarginContainer
class_name AddNodeMenu class_name AddNodeMenu
## A menu for adding nodes with a search bar.
@onready var search_line_edit: LineEdit = %SearchLineEdit @onready var search_line_edit: LineEdit = %SearchLineEdit
@onready var scroll_content_container: VBoxContainer = %ScrollContentContainer @onready var scroll_content_container: VBoxContainer = %ScrollContentContainer
@onready var scroll_container: ScrollContainer = $VBoxContainer/ScrollContainer @onready var scroll_container: ScrollContainer = $VBoxContainer/ScrollContainer
## The categories currently shown in the menu.
var categories: Dictionary = {} # Dictionary[String, Category] var categories: Dictionary = {} # Dictionary[String, Category]
## A list of categories to remember the collapsed state of so they remain collapsed when the search list is rebuilt.
var collapsed_categories: Array[String] var collapsed_categories: Array[String]
## Emitted when a node is selected either by clicking on it or pressing [constant @GlobalScope.KEY_ENTER] while the [member search_line_edit] is focused.
signal node_selected(type: String) signal node_selected(type: String)
@ -15,6 +20,7 @@ func _ready() -> void:
search("") search("")
## Add a new category to the menu.
func add_category(category_name: String) -> void: func add_category(category_name: String) -> void:
var c := Category.new(category_name.capitalize()) var c := Category.new(category_name.capitalize())
categories[category_name] = c categories[category_name] = c
@ -30,11 +36,13 @@ 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) -> void: func add_category_item(category: String, item: String, tooltip: String = "", favorite: bool = false) -> void:
var c: Category = categories[category] var c: Category = categories[category]
c.add_item(item, tooltip, favorite) c.add_item(item, tooltip, favorite)
## 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) -> void: func add_item(category: String, item: String, tooltip: String = "", favorite: bool = false) -> void:
if !categories.has(category): if !categories.has(category):
add_category(category) add_category(category)
@ -42,15 +50,18 @@ func add_item(category: String, item: String, tooltip: String = "", favorite: bo
add_category_item(category, item, tooltip, favorite) add_category_item(category, item, tooltip, favorite)
## Get a [AddNodeMenu.Category] node by its' identifier.
func get_category(category: String) -> Category: func get_category(category: String) -> Category:
return categories[category] return categories[category]
## Focus the search bar and select all its' text.
func focus_search_bar() -> void: func focus_search_bar() -> void:
search_line_edit.select_all() search_line_edit.select_all()
search_line_edit.grab_focus() search_line_edit.grab_focus()
## Searches for a node using [SearchProvider] and puts the results as items.
func search(text: String) -> void: func search(text: String) -> void:
scroll_content_container.get_children().map(func(c: Node): c.queue_free()) scroll_content_container.get_children().map(func(c: Node): c.queue_free())
categories.clear() categories.clear()
@ -69,6 +80,7 @@ func search(text: String) -> void:
get_category(categories.keys()[0]).highlight_item(0) get_category(categories.keys()[0]).highlight_item(0)
## Callback for [member search_line_edit]'s input events. Handles highlighting items when navigating with up/down arrow keys.
func _on_search_line_edit_gui_input(event: InputEvent) -> void: func _on_search_line_edit_gui_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_down"): if event.is_action_pressed("ui_down"):
var category: Category var category: Category
@ -123,6 +135,7 @@ func _on_search_line_edit_gui_input(event: InputEvent) -> void:
scroll_container.ensure_control_visible(category.get_child(item - 1)) scroll_container.ensure_control_visible(category.get_child(item - 1))
## Callback for [member search_line_edit]. Handles emitting [signal node_selected]
func _on_search_line_edit_text_submitted(_new_text: String) -> void: func _on_search_line_edit_text_submitted(_new_text: String) -> void:
var category: Category var category: Category
for i: String in categories: for i: String in categories:
@ -139,15 +152,22 @@ func _on_category_collapse_toggled(collapsed: bool, category: String) -> void:
else: else:
collapsed_categories.erase(category) collapsed_categories.erase(category)
## A collapsible item menu.
##
## An analog to [Tree] and [ItemList] made with nodes. Allows collapsing its
## children [AddNodeMenu.CategoryItem] nodes and highlighting them.[br]
## [b]Note:[/b] only one [AddNodeMenu.CategoryItem] can be highlighted at any time.
class Category extends VBoxContainer: class Category extends VBoxContainer:
const COLLAPSE_ICON := preload("res://graph_node_renderer/textures/collapse-icon.svg") const COLLAPSE_ICON := preload("res://graph_node_renderer/textures/collapse-icon.svg")
const COLLAPSE_ICON_COLLAPSED := preload("res://graph_node_renderer/textures/collapse-icon-collapsed.svg") const COLLAPSE_ICON_COLLAPSED := preload("res://graph_node_renderer/textures/collapse-icon-collapsed.svg")
var collapse_button: Button var collapse_button: Button
## Emitted when a child item has been pressed.
signal item_pressed(item: int) signal item_pressed(item: int)
## Emitted when a child item's favorite button has been pressed.
signal item_favorite_button_toggled(item: int, toggled: bool) signal item_favorite_button_toggled(item: int, toggled: bool)
## Emitted when the category's collapsed state has been toggled.
signal collapse_toggled(collapsed: bool) signal collapse_toggled(collapsed: bool)
@ -171,6 +191,7 @@ class Category extends VBoxContainer:
collapse_button.toggled.connect(set_collapsed) collapse_button.toggled.connect(set_collapsed)
## If [param collapsed] is [code]true[/code], collapses the category, hiding its children.
func set_collapsed(collapsed: bool) -> void: func set_collapsed(collapsed: bool) -> void:
collapse_button.icon = COLLAPSE_ICON_COLLAPSED if collapsed else COLLAPSE_ICON collapse_button.icon = COLLAPSE_ICON_COLLAPSED if collapsed else COLLAPSE_ICON
collapse_button.set_pressed_no_signal(collapsed) collapse_button.set_pressed_no_signal(collapsed)
@ -178,6 +199,7 @@ class Category extends VBoxContainer:
c.visible = !collapsed c.visible = !collapsed
## Add an item to the category.
func add_item(p_name: String, p_tooltip: String, p_favorite: bool = false) -> void: func add_item(p_name: String, p_tooltip: String, p_favorite: bool = false) -> void:
var item := CategoryItem.new(p_name, p_tooltip, p_favorite) var item := CategoryItem.new(p_name, p_tooltip, p_favorite)
item.favorite_toggled.connect( item.favorite_toggled.connect(
@ -191,38 +213,46 @@ class Category extends VBoxContainer:
add_child(item) add_child(item)
## Set an item's metadata at index [param item].
func set_item_metadata(item: int, key: StringName, metadata: Variant) -> void: func set_item_metadata(item: int, key: StringName, metadata: Variant) -> void:
get_child(item).set_meta(key, metadata) get_child(item).set_meta(key, metadata)
## Retrieve an item's metadata at index [param item].
func get_item_metadata(item: int, key: StringName) -> Variant: func get_item_metadata(item: int, key: StringName) -> Variant:
return get_child(item).get_meta(key) return get_child(item).get_meta(key)
## Get the amount of items in this category.
func get_item_count() -> int: func get_item_count() -> int:
return get_child_count() return get_child_count()
## Toggle an item at index [param item]'s favorite state.
func set_item_favorite(item:int, favorite: bool) -> void: func set_item_favorite(item:int, favorite: bool) -> void:
var _item := get_child(item) as CategoryItem var _item := get_child(item) as CategoryItem
_item.set_favorite(favorite) _item.set_favorite(favorite)
## Returns [code]true[/code] if the item at [param item] is marked as favorite.
func is_item_favorite(item: int) -> bool: func is_item_favorite(item: int) -> bool:
var _item := get_child(item) as CategoryItem var _item := get_child(item) as CategoryItem
return _item.is_favorite() return _item.is_favorite()
## Highlight item at index [item], and unhighlight all other items.
func highlight_item(item: int) -> void: func highlight_item(item: int) -> void:
for c: CategoryItem in get_children(): for c: CategoryItem in get_children():
c.set_highlighted(c.get_index() == item) c.set_highlighted(c.get_index() == item)
## Unhighlight all items.
func unhighlight_all() -> void: func unhighlight_all() -> void:
for c: CategoryItem in get_children(): for c: CategoryItem in get_children():
c.set_highlighted(false) c.set_highlighted(false)
## Returns the index of the currently highlighted item. Returns [code]-1[/code] if no item is highlighted in this category.
func get_highlighted_item() -> int: func get_highlighted_item() -> int:
for c: CategoryItem in get_children(): for c: CategoryItem in get_children():
if c.is_highlighted: if c.is_highlighted:
@ -231,11 +261,15 @@ class Category extends VBoxContainer:
return -1 return -1
## Represents an item in a [AddNodeMenu.Category].
##
## A selectable and highlightable category item with a favorite button.
class CategoryItem extends HBoxContainer: class CategoryItem extends HBoxContainer:
const FAVORITE_ICON := preload("res://graph_node_renderer/textures/favorite-icon.svg") const FAVORITE_ICON := preload("res://graph_node_renderer/textures/favorite-icon.svg")
const NON_FAVORITE_ICON := preload("res://graph_node_renderer/textures/non-favorite-icon.svg") const NON_FAVORITE_ICON := preload("res://graph_node_renderer/textures/non-favorite-icon.svg")
const ITEM_MARGIN := 16 const ITEM_MARGIN := 16
## The stylebox to use if this item is highlighted.
var highlighted_stylebox := StyleBoxFlat.new() var highlighted_stylebox := StyleBoxFlat.new()
var is_highlighted: bool var is_highlighted: bool
@ -244,7 +278,9 @@ class CategoryItem extends HBoxContainer:
var name_button: Button var name_button: Button
var panel: PanelContainer var panel: PanelContainer
## Emitted when this item has been pressed.
signal pressed signal pressed
## Emitted when this item's [member fav_button] has been pressed.
signal favorite_toggled(toggled: bool) signal favorite_toggled(toggled: bool)
@ -289,15 +325,18 @@ class CategoryItem extends HBoxContainer:
add_child(panel) add_child(panel)
## Toggle this item's favorite state.
func set_favorite(favorite: bool) -> void: func set_favorite(favorite: bool) -> void:
fav_button.icon = FAVORITE_ICON if favorite else NON_FAVORITE_ICON fav_button.icon = FAVORITE_ICON if favorite else NON_FAVORITE_ICON
fav_button.set_pressed_no_signal(favorite) fav_button.set_pressed_no_signal(favorite)
## Returns [code]true[/code] if this item is marked as favorite.
func is_favorite() -> bool: func is_favorite() -> bool:
return fav_button.icon == FAVORITE_ICON return fav_button.icon == FAVORITE_ICON
## Toggle this item's highlighted state.
func set_highlighted(highlighted: bool) -> void: func set_highlighted(highlighted: bool) -> void:
is_highlighted = highlighted is_highlighted = highlighted
if highlighted: if highlighted: