390 lines
10 KiB
GDScript
390 lines
10 KiB
GDScript
###
|
|
### a set of random utility functions
|
|
###
|
|
extends Object
|
|
|
|
class_name GDBUtility
|
|
|
|
################
|
|
# public stuff #
|
|
################
|
|
func _init() -> void:
|
|
assert(0, "This class should not be instantiated.")
|
|
|
|
static func find_node_filter(root : Node, object : Object, method : String, bfs := false) -> Node:
|
|
if object.call(method, root):
|
|
return root
|
|
|
|
if bfs:
|
|
for child in root.get_children():
|
|
if object.call(method, child):
|
|
return child
|
|
|
|
for child in root.get_children():
|
|
var res = find_node_filter(child, object, method)
|
|
if res != null:
|
|
return res
|
|
return null
|
|
|
|
static func find_node_by_type(root : Node, tp, bfs := false) -> Node:
|
|
if root is tp:
|
|
return root
|
|
|
|
if bfs:
|
|
for child in root.get_children():
|
|
if child is tp:
|
|
return child
|
|
|
|
for child in root.get_children():
|
|
var res = find_node_by_type(child, tp, bfs)
|
|
if res != null:
|
|
return res
|
|
return null
|
|
|
|
static func find_node_by_name(root : Node, name : String, bfs := false, use_cache := true) -> Node:
|
|
if root.name == name:
|
|
return root
|
|
|
|
# create the cache regardless of use_cache
|
|
if !root.has_meta("__find_by_name_node_cache"):
|
|
root.set_meta("__find_by_name_node_cache", {})
|
|
|
|
var cache = root.get_meta("__find_by_name_node_cache")
|
|
if use_cache && name in cache:
|
|
var node = root.get_node(cache[name])
|
|
if node:
|
|
return node
|
|
else:
|
|
cache.erase(name)
|
|
|
|
if bfs:
|
|
for child in root.get_children():
|
|
if child.name == name:
|
|
cache[name] = child
|
|
return child
|
|
|
|
for child in root.get_children():
|
|
var res = find_node_by_name(child, name)
|
|
if res != null:
|
|
cache[name] = root.get_path_to(res)
|
|
return res
|
|
return null
|
|
|
|
static func find_nodes_by_type(root : Node, tp, as_path = false) -> Array:
|
|
var result = []
|
|
__find_nodes_by_type(root, tp, as_path, result)
|
|
return result
|
|
|
|
static func find_nodes_with_member(root : Node, member : String) -> Array:
|
|
var result = []
|
|
__find_nodes_with_member(root, member, result)
|
|
return result
|
|
|
|
static func find_parent_with_method(root : Node, method : String) -> Node:
|
|
if root.has_method(method):
|
|
return root
|
|
else:
|
|
var parent = root.get_parent()
|
|
if parent == null:
|
|
return null
|
|
return find_parent_with_method(parent, method)
|
|
|
|
static func find_parent_with_member(root : Node, member : String) -> Node:
|
|
if root.get(member) != null:
|
|
return root
|
|
else:
|
|
var parent = root.get_parent()
|
|
if parent == null:
|
|
return null
|
|
return find_parent_with_member(parent, member)
|
|
|
|
static func find_parent_with_name(root : Node, name : String) -> Node:
|
|
if root.name == name:
|
|
return root
|
|
else:
|
|
var parent = root.get_parent()
|
|
if parent == null:
|
|
return null
|
|
return find_parent_with_name(parent, name)
|
|
|
|
static func find_parent_with_type(root : Node, tp) -> Node:
|
|
if (tp is Script && tp.instance_has(root)) \
|
|
|| root is tp:
|
|
return root
|
|
else:
|
|
var parent = root.get_parent()
|
|
if parent == null:
|
|
return null
|
|
return find_parent_with_type(parent, tp)
|
|
|
|
static func find_treeitem_with_metadata(root : TreeItem, metadata, column := 0) -> TreeItem:
|
|
while root:
|
|
if root.get_metadata(column) == metadata:
|
|
return root
|
|
var in_children := find_treeitem_with_metadata(root.get_children(), metadata, column)
|
|
if in_children:
|
|
return in_children
|
|
root = root.get_next()
|
|
return null
|
|
|
|
static func hide_properties(properties : Array, names_to_hide : Array) -> void:
|
|
for i in range(properties.size() - 1, -1, -1):
|
|
if properties[i]["name"] in names_to_hide:
|
|
properties.remove(i)
|
|
|
|
static func type_name(type : int) -> String:
|
|
match type:
|
|
TYPE_NIL:
|
|
return "null"
|
|
TYPE_BOOL:
|
|
return "bool"
|
|
TYPE_INT:
|
|
return "int"
|
|
TYPE_REAL:
|
|
return "float"
|
|
TYPE_STRING:
|
|
return "String"
|
|
TYPE_VECTOR2:
|
|
return "Vector2"
|
|
TYPE_RECT2:
|
|
return "Rect2"
|
|
TYPE_VECTOR3:
|
|
return "Vector3"
|
|
TYPE_TRANSFORM2D:
|
|
return "Transform2D"
|
|
TYPE_PLANE:
|
|
return "Plane"
|
|
TYPE_QUAT:
|
|
return "Quat"
|
|
TYPE_AABB:
|
|
return "AABB"
|
|
TYPE_BASIS:
|
|
return "Basis"
|
|
TYPE_TRANSFORM:
|
|
return "Transform"
|
|
TYPE_COLOR:
|
|
return "Color"
|
|
TYPE_NODE_PATH:
|
|
return "NodePath"
|
|
TYPE_RID:
|
|
return "RID"
|
|
TYPE_OBJECT:
|
|
return "Object"
|
|
TYPE_DICTIONARY:
|
|
return "Dictionary"
|
|
TYPE_ARRAY:
|
|
return "Array"
|
|
TYPE_RAW_ARRAY:
|
|
return "PoolByteArray"
|
|
TYPE_INT_ARRAY:
|
|
return "PoolIntArray"
|
|
TYPE_REAL_ARRAY:
|
|
return "PoolRealArray"
|
|
TYPE_STRING_ARRAY:
|
|
return "PoolStringArray"
|
|
TYPE_VECTOR2_ARRAY:
|
|
return "Vector2Array"
|
|
TYPE_VECTOR3_ARRAY:
|
|
return "Vector3Array"
|
|
TYPE_COLOR_ARRAY:
|
|
return "ColorArray"
|
|
_:
|
|
return "Unknown"
|
|
|
|
static func format_type(type : Dictionary, none_name := "void") -> String:
|
|
match type["type"]:
|
|
TYPE_NIL:
|
|
return none_name
|
|
TYPE_OBJECT:
|
|
return type.get("class_name", "Object")
|
|
var tp:
|
|
return type_name(tp)
|
|
|
|
static func format_method_signature(method : Dictionary, format := "{return} {name}({args})") -> String:
|
|
var args := PoolStringArray()
|
|
var rettype := "void"
|
|
if method.has("return"):
|
|
rettype = format_type(method["return"])
|
|
for i in range(method["args"].size()):
|
|
var arg = method["args"][i]
|
|
var def = ""
|
|
if i < method["default_args"].size():
|
|
def = " = %s" % str(method["default_args"][i])
|
|
args.append("{name}: {type}{def}".format({
|
|
"name": arg["name"],
|
|
"type": format_type(arg, "Variant"),
|
|
"def": def
|
|
}))
|
|
return format.format({
|
|
"return": rettype,
|
|
"name": method["name"],
|
|
"args": args.join(", ")
|
|
})
|
|
|
|
static func format_signal_signature(sig : Dictionary) -> String:
|
|
return format_method_signature(sig, "{name}({args})")
|
|
|
|
static func get_type_property_list(type : Dictionary) -> Array:
|
|
match type["type"]:
|
|
TYPE_VECTOR2:
|
|
return [
|
|
{"name": "x", "type": TYPE_REAL},
|
|
{"name": "y", "type": TYPE_REAL}
|
|
]
|
|
TYPE_RECT2:
|
|
return [
|
|
{"name": "position", "type": TYPE_VECTOR2},
|
|
{"name": "size", "type": TYPE_VECTOR2},
|
|
{"name": "end", "type": TYPE_VECTOR2}
|
|
]
|
|
TYPE_VECTOR3:
|
|
return [
|
|
{"name": "x", "type": TYPE_REAL},
|
|
{"name": "y", "type": TYPE_REAL},
|
|
{"name": "z", "type": TYPE_REAL}
|
|
]
|
|
TYPE_TRANSFORM2D:
|
|
return [
|
|
{"name": "x", "type": TYPE_VECTOR2},
|
|
{"name": "y", "type": TYPE_VECTOR2},
|
|
{"name": "origin", "type": TYPE_VECTOR2}
|
|
]
|
|
TYPE_PLANE:
|
|
return [
|
|
{"name": "normal", "type": TYPE_VECTOR3},
|
|
{"name": "x", "type": TYPE_REAL},
|
|
{"name": "y", "type": TYPE_REAL},
|
|
{"name": "z", "type": TYPE_REAL},
|
|
{"name": "d", "type": TYPE_REAL}
|
|
]
|
|
TYPE_QUAT:
|
|
return [
|
|
{"name": "x", "type": TYPE_REAL},
|
|
{"name": "y", "type": TYPE_REAL},
|
|
{"name": "z", "type": TYPE_REAL},
|
|
{"name": "w", "type": TYPE_REAL}
|
|
]
|
|
TYPE_AABB:
|
|
return [
|
|
{"name": "position", "type": TYPE_VECTOR3},
|
|
{"name": "size", "type": TYPE_VECTOR3},
|
|
{"name": "end", "type": TYPE_VECTOR3}
|
|
]
|
|
TYPE_BASIS:
|
|
return [
|
|
{"name": "x", "type": TYPE_VECTOR3},
|
|
{"name": "y", "type": TYPE_VECTOR3},
|
|
{"name": "z", "type": TYPE_VECTOR3}
|
|
]
|
|
TYPE_TRANSFORM:
|
|
return [
|
|
{"name": "basis", "type": TYPE_BASIS},
|
|
{"name": "origin", "type": TYPE_VECTOR3}
|
|
]
|
|
TYPE_COLOR:
|
|
return [
|
|
{"name": "r", "type": TYPE_REAL},
|
|
{"name": "g", "type": TYPE_REAL},
|
|
{"name": "b", "type": TYPE_REAL},
|
|
{"name": "a", "type": TYPE_REAL},
|
|
{"name": "h", "type": TYPE_REAL},
|
|
{"name": "s", "type": TYPE_REAL},
|
|
{"name": "v", "type": TYPE_REAL},
|
|
{"name": "r8", "type": TYPE_INT},
|
|
{"name": "g8", "type": TYPE_INT},
|
|
{"name": "b8", "type": TYPE_INT},
|
|
{"name": "a8", "type": TYPE_INT}
|
|
]
|
|
TYPE_OBJECT:
|
|
if type.has("class_name"):
|
|
return ClassDB.class_get_property_list(type["class_name"])
|
|
return []
|
|
|
|
static func get_property_type(obj : Object, property : String) -> int:
|
|
return typeof(obj.get(property)) # TODO
|
|
|
|
static func get_method_arg_types(obj : Object, method : String) -> Array:
|
|
var methods := obj.get_method_list()
|
|
var types := []
|
|
for ele in methods:
|
|
if ele["name"] == method:
|
|
for arg in ele["args"]:
|
|
types.append(arg["type"])
|
|
break
|
|
return types
|
|
|
|
static func get_scene_tree() -> SceneTree:
|
|
return Engine.get_main_loop() as SceneTree
|
|
|
|
static func wait_frames(nframes : int) -> void:
|
|
var tree := get_scene_tree()
|
|
for i in range(max(1, nframes)): # yield at least once so this always returns a GDScriptFunctionState
|
|
yield(tree, "idle_frame")
|
|
|
|
static func disconnect_all(sender : Object, receiver : Object, signal_name := "") -> void:
|
|
if signal_name == "":
|
|
for sig in sender.get_signal_list():
|
|
disconnect_all(sender, receiver, sig["name"])
|
|
return
|
|
|
|
for connection in sender.get_signal_connection_list(signal_name):
|
|
if connection["target"] != receiver:
|
|
continue
|
|
sender.disconnect(signal_name, receiver, connection["method"])
|
|
|
|
static func try_connect(sender : Object, signal_name : String, receiver : Object, method : String, binds := []):
|
|
if sender.has_signal(signal_name):
|
|
sender.connect(signal_name, receiver, method, binds)
|
|
|
|
static func copy_signal_handlers(target : Object, source : Object, sig_name : String) -> void:
|
|
for connection in source.get_signal_connection_list(sig_name):
|
|
target.connect(sig_name, connection["target"], connection["method"], connection["binds"])
|
|
|
|
static func copy_all_signal_handlers(target : Object, source : Object, sig_names : Array) -> void:
|
|
for sig_name in sig_names:
|
|
copy_signal_handlers(target, source, sig_name)
|
|
|
|
static func control_is_in_front_of(front : Control, back : Control) -> bool:
|
|
var front_popup : bool = find_parent_with_type(front, Popup) != null
|
|
var back_popup : bool = find_parent_with_type(back, Popup) != null
|
|
|
|
if front_popup && !back_popup:
|
|
return true
|
|
elif !front_popup && back_popup:
|
|
return false
|
|
elif front_popup && back_popup:
|
|
return popup_is_in_front_of(front, back)
|
|
else:
|
|
return back.is_greater_than(front)
|
|
|
|
static func popup_is_in_front_of(front : Popup, back : Popup) -> bool:
|
|
return back.is_greater_than(front) # no idea?!
|
|
|
|
static func get_state_object(meta_key : String, type):
|
|
var tree := get_scene_tree()
|
|
if !tree.has_meta(meta_key):
|
|
tree.set_meta(meta_key, type.new())
|
|
return tree.get_meta(meta_key)
|
|
|
|
static func translate(text : String) -> String:
|
|
return get_scene_tree().root.tr(text)
|
|
|
|
#################
|
|
# private stuff #
|
|
#################
|
|
static func __find_nodes_by_type(root : Node, tp, as_path, result):
|
|
if root is tp:
|
|
if as_path:
|
|
result.append(root.get_path())
|
|
else:
|
|
result.append(root)
|
|
for child in root.get_children():
|
|
__find_nodes_by_type(child, tp, as_path, result)
|
|
return result
|
|
|
|
static func __find_nodes_with_member(root : Node, member : String, result):
|
|
if root.get(member) != null:
|
|
result.append(root)
|
|
for child in root.get_children():
|
|
__find_nodes_with_member(child, member, result)
|