2021-08-21 17:13:19 +02:00

386 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 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)