diff --git a/.godot_ide/.gdignore b/.godot_ide/.gdignore
new file mode 100644
index 0000000..a0d111a
--- /dev/null
+++ b/.godot_ide/.gdignore
@@ -0,0 +1 @@
+IDE CONFIG
\ No newline at end of file
diff --git a/.godot_ide/config.ini b/.godot_ide/config.ini
new file mode 100644
index 0000000..c230836
--- /dev/null
+++ b/.godot_ide/config.ini
@@ -0,0 +1,12 @@
+[config]
+
+plugin={
+"res://addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/macro-n/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/quick_folds/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/script_splitter/plugin.gd": true,
+"res://addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd": true
+}
diff --git a/addons/_Godot-IDE_/IDE.gd b/addons/_Godot-IDE_/IDE.gd
new file mode 100644
index 0000000..565c7ba
--- /dev/null
+++ b/addons/_Godot-IDE_/IDE.gd
@@ -0,0 +1,683 @@
+@tool
+class_name IDE extends EditorPlugin
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# ./plugins: Folder for extensions.
+# ./shared_resources: Free use for any purposes.
+# =============================================================================
+
+const EDITOR_CONFIG_PATH : String = "plugin/godot_ide/"
+const PATH_CONFIG : String = "res://.ide/"
+
+static var _ref : Dictionary[String, Object] = {}
+static var _safe_ref : Dictionary[String, Node] = {}
+
+static var debug : bool = true
+
+static var PRIVATE_METHODS : String = "__"
+static var VIRTUAL_METHODS : String = "_"
+
+static var _menu : MenuButton = null
+
+#region DEV_API
+## Get current editor container for edit/view scripts.
+static func get_script_editor_container() -> TabContainer:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("script_editor_container", script_editor, "*", "TabContainer", 0)
+ if out is TabContainer:
+ return out
+ return null
+
+## Get current script/documents opened list.
+static func get_script_list() -> ItemList:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("script_list", script_editor, "*", "ItemList", 0)
+ if out is ItemList:
+ return out
+ return null
+
+## Get search bar native of script list.
+static func get_script_list_search_bar() -> LineEdit:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("script_list_search_bar", script_editor, "*", "LineEdit", 0)
+ if out is LineEdit:
+ return out
+ return null
+
+## Get label of the script list
+static func get_script_list_current_label() -> Label:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("script_list_current_label", script_editor, "*", "Label", 2)
+ if out is Label:
+ return out
+ return null
+
+## Get the bottom panel of filter side by script list.
+static func get_filter_methods() -> ItemList:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("filter_methods", script_editor, "*", "ItemList", 1)
+ if out is ItemList:
+ return out
+ return null
+
+## Get the search bar of the filters method panel.
+static func get_filter_methods_search_bar() -> LineEdit:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("filter_methods_search_bar", script_editor, "*", "LineEdit", 1)
+ if out is LineEdit:
+ return out
+ return null
+
+## Get the file on top menu bar.
+static func get_file_menu_button() -> MenuButton:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("file_menu_button", script_editor, "*", "MenuButton", 0)
+ if out is MenuButton:
+ return out
+ return null
+
+## Get the edit on top menu bar.
+static func get_edit_menu_button() -> MenuButton:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("edit_menu_button", script_editor, "*", "MenuButton", 1)
+ if out is MenuButton:
+ return out
+ return null
+
+## Get the search on top menu bar.
+static func get_search_menu_button() -> MenuButton:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("search_menu_button", script_editor, "*", "MenuButton", 2)
+ if out is MenuButton:
+ return out
+ return null
+
+## Get the got to menu on top menu bar.
+static func get_go_to_menu_button() -> MenuButton:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("get_go_to_menu_button", script_editor, "*", "MenuButton", 3)
+ if out is MenuButton:
+ return out
+ return null
+
+## Get the debug on top menu bar.
+static func get_debug_menu_button() -> MenuButton:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("debug_menu_button", script_editor, "*", "MenuButton", 17)
+ if out is MenuButton:
+ return out
+ return null
+
+## Get the container of scripts list.
+static func get_script_list_container() -> VSplitContainer:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("script_list_container", script_editor, "*", "VSplitContainer", 0)
+ if out is VSplitContainer:
+ return out
+ return null
+
+## Get the container of the editor.
+static func get_editor_container() -> HSplitContainer:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var out : Variant = _get_reference("editor_container", script_editor, "*", "HSplitContainer", 0)
+ if out is HSplitContainer:
+ return out
+ return null
+
+## Get native screen size. Raito must be 0.1 - 1.0
+static func get_screen_size(ratio : float = 1.0) -> Vector2:
+ var screen : Vector2 = DisplayServer.screen_get_size()
+ return screen * ratio
+
+## Clamp native screen size.
+static func clamp_screen_size(current_size : Vector2, min_ratio : float = 0.0, max_ratio : float = 1.0) -> Vector2:
+ var screen : Vector2 = DisplayServer.screen_get_size()
+ var max_screen : Vector2 = screen * max_ratio
+ var min_screen : Vector2 = screen * min_ratio
+ return Vector2(max(min(current_size.x, max_screen.x), min_screen.x), max(min(current_size.y, max_screen.y), min_screen.y))
+
+## Get full properties headers of a script.
+static func get_script_properties_list(script : Script, full : bool = true, check_is_native : bool = true) -> Dictionary:
+ if script == null:
+ return {}
+ var nname : String = get_name_script(script)
+ if check_is_native and ClassDB.class_exists(nname):
+ return _generate_native(nname)
+ if full:
+ var data : Dictionary = _generate(script)
+ return _generate_native(script.get_instance_base_type(), data, data.size()-1)
+ else:
+ return _generate(script)
+
+## Get editor menu of godot-ide-extension from editor container.
+static func get_menu_button() -> MenuButton:
+ return _menu
+#endregion
+
+
+# =============================================================================
+#region DEV_CONFIG
+static func set_config(plugin_name : String, config_name : String, value : Variant) -> void:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ if config_name.is_empty():
+ printerr("Config name can not be empty!")
+ return
+ if plugin_name.is_empty():
+ printerr("Plugin name can not be empty")
+ return
+ editor.set_setting(EDITOR_CONFIG_PATH.path_join(plugin_name).path_join(config_name), value)
+
+static func add_property_config_info(plugin_name : String, config_name : String, type : Variant.Type, hint : int, hint_string : String):
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ if config_name.is_empty():
+ printerr("Config name can not be empty!")
+ return
+ if plugin_name.is_empty():
+ printerr("Plugin name can not be empty")
+ return
+ editor.add_property_info({
+ "name": EDITOR_CONFIG_PATH.path_join(plugin_name).path_join(config_name),
+ "type": type,
+ "hint": hint,
+ "hint_string": hint_string
+ })
+
+static func get_config(addon_name : String, config_name : String) -> Variant:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ var setting : String = EDITOR_CONFIG_PATH.path_join(addon_name).path_join(config_name)
+ if editor.has_setting(setting):
+ return editor.get_setting(setting)
+ return null
+
+static func set_file_config_value(section : String, key : String, value : Variant) -> int:
+ if !DirAccess.dir_exists_absolute(PATH_CONFIG):
+ if OK != DirAccess.make_dir_recursive_absolute(PATH_CONFIG):
+ push_warning("Can not creates config dir!")
+ return 1
+ if FileAccess.file_exists(PATH_CONFIG.path_join(".gdignore")):
+ var file : FileAccess = FileAccess.open(PATH_CONFIG.path_join(".gdignore"), FileAccess.WRITE)
+ file.store_string("FOLDER Godot-IDE CONFIG")
+ file.close()
+
+ var cfg_path : String = PATH_CONFIG.path_join("config.ini")
+ var cfg : ConfigFile = ConfigFile.new()
+ if FileAccess.file_exists(cfg_path):
+ cfg.load(cfg_path)
+ cfg.set_value(section, key, value)
+ return cfg.save(cfg_path)
+
+static func get_file_config_value(section : String, key : String) -> Variant:
+ var cfg_path : String = PATH_CONFIG.path_join("config.ini")
+ if !FileAccess.file_exists(cfg_path):
+ return null
+ var cfg : ConfigFile = ConfigFile.new()
+ var err : int = cfg.load(cfg_path)
+ if OK != err:
+ return null
+ return cfg.get_value(section, key, "")
+
+#endregion
+
+#region utils
+
+
+static func get_type(_typeof : int) -> String:
+ var txt : String = ""
+ match _typeof:
+ TYPE_BOOL : txt = "bool"
+ TYPE_INT : txt = "int"
+ TYPE_FLOAT: txt = "float"
+ TYPE_STRING : txt = "String"
+ TYPE_VECTOR2 : txt = "Vector2"
+ TYPE_VECTOR2I : txt = "Vector2i"
+ TYPE_RECT2 : txt = "Rect2"
+ TYPE_RECT2I : txt = "Rect2i"
+ TYPE_VECTOR3 : txt = "Vector3"
+ TYPE_VECTOR3I : txt = "Vector3i"
+ TYPE_TRANSFORM2D : txt = "Tranform2D"
+ TYPE_VECTOR4 : txt = "Vector4"
+ TYPE_VECTOR4I : txt = "Vector4i"
+ TYPE_PLANE : txt = "Plane"
+ TYPE_QUATERNION : txt = "Quaternion"
+ TYPE_AABB : txt = "AABB"
+ TYPE_BASIS : txt = "Basis"
+ TYPE_TRANSFORM3D : txt = "Transform3D"
+ TYPE_PROJECTION : txt = "Projection"
+ TYPE_COLOR : txt = "Color"
+ TYPE_STRING_NAME : txt = "StringName"
+ TYPE_NODE_PATH : txt = "NodePath"
+ TYPE_RID : txt = "RID"
+ TYPE_OBJECT : txt = "Object"
+ TYPE_CALLABLE : txt = "Callable"
+ TYPE_SIGNAL : txt = "Signal"
+ TYPE_DICTIONARY : txt = "Dictionary"
+ TYPE_ARRAY : txt = "Array"
+ TYPE_PACKED_BYTE_ARRAY : txt = "PackedByteArray"
+ TYPE_PACKED_INT32_ARRAY : txt = "PackedInt32Array"
+ TYPE_PACKED_INT64_ARRAY : txt = "PackedInt64Array"
+ TYPE_PACKED_FLOAT32_ARRAY : txt = "PackedFloat32Array"
+ TYPE_PACKED_FLOAT64_ARRAY : txt = "PackedFloat64Array"
+ TYPE_PACKED_STRING_ARRAY : txt = "PackedStringArray"
+ TYPE_PACKED_VECTOR2_ARRAY : txt = "PackedVector2Array"
+ TYPE_PACKED_VECTOR3_ARRAY : txt = "PackedVector3Array"
+ TYPE_PACKED_COLOR_ARRAY : txt = "PackedColorArray"
+ TYPE_PACKED_VECTOR4_ARRAY : txt = "PackedVector4Array"
+ _:
+ txt = "Variant"
+ return txt
+
+static func _generate(script : Script, data : Dictionary = {}, index : int = -1) -> Dictionary:
+ if script == null:
+ return data
+ var funcs : Dictionary = {}
+ var props : Dictionary = {}
+ var signals : Dictionary = {}
+ var constants : Dictionary = {}
+ var base : Dictionary = {
+ "name" : &"GDScript"
+ ,"functions" : funcs
+ ,"properties" : props
+ ,"signals" : signals
+ ,"constants" : constants
+ ,"type": 1
+ ,"custom": false
+ ,"path" : script.resource_path
+ ,"tool" : script.is_tool()
+ ,"abstract" : script.is_abstract()
+ ,"built_in" : script.is_built_in()
+ }
+
+ var base_name : String = get_name_script(script, base)
+ base["name"] = base_name
+ index += 1
+ data[index] = base
+
+ for dict: Dictionary in script.get_script_method_list():
+ var func_name: StringName = dict.name
+ if func_name.begins_with("@"):continue
+ funcs[func_name] =_get_header_virtual(dict)
+ if dict.has("flags"):
+ if dict["flags"] & METHOD_FLAG_STATIC:
+ funcs[func_name] += "||static"
+ elif dict["flags"] & METHOD_FLAG_CONST:
+ funcs[func_name] += "||const"
+
+ for dict : Dictionary in script.get_script_property_list():
+ var pro_name: StringName = dict.name
+ var as_exporrt : bool = false
+ if !pro_name.get_extension().is_empty():
+ continue
+ if dict.has("usage"):
+ var usage : int = dict["usage"]
+ if !(usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
+ continue
+ for x : int in [PROPERTY_USAGE_STORAGE, PROPERTY_USAGE_EDITOR, PROPERTY_USAGE_CHECKABLE, PROPERTY_USAGE_CHECKED, PROPERTY_USAGE_GROUP]:
+ if usage & x:
+ as_exporrt = true
+ break
+ props[pro_name] =_get_header_virtual(dict)
+ if as_exporrt:
+ props[pro_name] += "||export"
+ else:
+ if dict.has("flags"):
+ if dict["flags"] & METHOD_FLAG_STATIC:
+ props[pro_name] += "||static"
+ elif dict["flags"] & METHOD_FLAG_CONST:
+ props[pro_name] += "||const"
+
+ for dict : Dictionary in script.get_property_list():
+ var pro_name: StringName = dict.name
+ if !pro_name.get_extension().is_empty():
+ continue
+ if dict.has("usage"):
+ var usage : int = dict["usage"]
+ if !(usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
+ continue
+ props[pro_name] =_get_header_virtual(dict)
+ props[pro_name] += "||static"
+
+ for dict : Dictionary in script.get_script_signal_list():
+ var pro_name: StringName = dict.name
+ signals[pro_name] =_get_header_virtual(dict)
+
+ for dict : Variant in script.get_script_constant_map():
+ if dict is StringName:
+ var variant : Variant = script.get(dict)
+ constants[dict] = "{0}||{1}".format([dict, get_type(typeof(variant))])
+ elif dict is Dictionary:
+ var pro_name: StringName = dict.name
+ constants[pro_name] =_get_header_virtual(dict)
+
+ if data.size() > 0:
+ var start : int = 0
+ while !data.has(start) and start < index:
+ start += 1
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["functions"]
+ for k : Variant in funcs.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["properties"]
+ for k : Variant in props.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["signals"]
+ for k : Variant in signals.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["constants"]
+ for k : Variant in constants.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ else:
+ clazz[k] = constants[k] + "||overrided"
+ return _generate(script.get_base_script(), data, index)
+
+
+static func _generate_native(native : StringName, data : Dictionary = {}, index : int = 0) -> Dictionary:
+ if native.is_empty() or !ClassDB.class_exists(native):
+ return data
+ var funcs : Dictionary = {}
+ var props : Dictionary = {}
+ var signals : Dictionary = {}
+ var constants : Dictionary = {}
+
+ var api_type : int = ClassDB.class_get_api_type(native)
+
+ var base : Dictionary = {
+ "name" : native
+ ,"functions" : funcs
+ ,"properties" : props
+ ,"signals" : signals
+ ,"constants" : constants
+ ,"type" : 0
+ ,"custom": false
+ ,"path" : "NativeScript"
+ ,"tool" : api_type == ClassDB.APIType.API_EDITOR or api_type == ClassDB.APIType.API_EDITOR_EXTENSION
+ ,"abstract" : false #!
+ ,"built_in" : false
+ }
+ index += 1
+ data[index] = base
+
+
+ for dict: Dictionary in ClassDB.class_get_method_list(native, true):
+ funcs[dict.name] =_get_header_virtual(dict)
+
+ for dict : Dictionary in ClassDB.class_get_property_list(native, true):
+ var pro_name: StringName = dict.name
+ if !pro_name.get_extension().is_empty():
+ continue
+ props[pro_name] =_get_header_virtual(dict)
+
+ for dict : Dictionary in ClassDB.class_get_signal_list(native, true):
+ var pro_name: StringName = dict.name
+ signals[pro_name] =_get_header_virtual(dict)
+
+ for dict : String in ClassDB.class_get_enum_list(native, true):
+ var pro_name: StringName = dict
+ constants[pro_name] = "{0}||enum||void".format([dict])
+
+ for dict : String in ClassDB.class_get_integer_constant_list(native, true):
+ var pro_name: StringName = dict
+ constants[pro_name] = "{0}||int||void".format([dict])
+
+ if data.size() > 0:
+ var start : int = 0
+ while !data.has(start) and start < index:
+ start += 1
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["functions"]
+ for k : Variant in funcs.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["properties"]
+ for k : Variant in props.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["signals"]
+ for k : Variant in signals.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+ for x : int in range(start, index, 1):
+ var clazz : Dictionary = data[x]["constants"]
+ for k : Variant in constants.keys():
+ if clazz.has(k):
+ if !"||overrided" in clazz[k]:
+ clazz[k] += "||overrided"
+
+ return _generate_native(ClassDB.get_parent_class(native), data, index)
+
+static func clamp_rect_to_screen(to_clamp_rect: Rect2, max_aviable_rect : Rect2) -> Rect2:
+
+ if to_clamp_rect.position.x < max_aviable_rect.position.x:
+ to_clamp_rect.position.x = max_aviable_rect.position.x
+ elif to_clamp_rect.position.x + to_clamp_rect.size.x > max_aviable_rect.position.x + max_aviable_rect.size.x:
+ to_clamp_rect.position.x = max_aviable_rect.position.x + max_aviable_rect.size.x - to_clamp_rect.size.x
+
+ if to_clamp_rect.position.y < max_aviable_rect.position.y:
+ to_clamp_rect.position.y = max_aviable_rect.position.y
+ elif to_clamp_rect.position.y + to_clamp_rect.size.y > max_aviable_rect.position.y + max_aviable_rect.size.y:
+ to_clamp_rect.position.y = max_aviable_rect.position.y + max_aviable_rect.size.y - to_clamp_rect.size.y
+
+ if to_clamp_rect.size.x > max_aviable_rect.size.x:
+ to_clamp_rect.position.x = max_aviable_rect.position.x + (max_aviable_rect.size.x - to_clamp_rect.size.x) / 2
+ if to_clamp_rect.size.y > max_aviable_rect.size.y:
+ to_clamp_rect.position.y = max_aviable_rect.position.y + (max_aviable_rect.size.y - to_clamp_rect.size.y) / 2
+
+ return to_clamp_rect
+
+
+static func get_header_function(dict : Dictionary) -> String:
+ var params : String = ""
+ var args : Array = dict["args"]
+ var separator : String = ""
+ var default_args : Array = dict["default_args"]
+ var _default_index : int = default_args.size()
+
+ for y : int in range(args.size() - 1, -1, -1):
+ var arg : Dictionary = args[y]
+ var txt : String = arg["name"]
+ if !(arg["class_name"]).is_empty():
+ txt += str(" : ", arg["class_name"] as String)
+ else:
+ var _typeof : int = arg["type"]
+ txt += str(" : ", IDE.get_type(_typeof))
+ if _default_index > 0:
+ _default_index -= 1
+ var def : Variant = default_args[_default_index]
+ var _type : int = typeof(def)
+ if def == null or _type < 1:
+ txt += str(' = null')
+ elif _type < 5:
+ if def is String:
+ txt += str(' = "', def, '"')
+ elif def is StringName:
+ txt += str(' = &"', def, '"')
+ else:
+ txt += str(" = ", def)
+ else:
+ txt += str(" = ", IDE.get_type(typeof(def)), def)
+ params = str(txt, separator, params)
+ separator = ", "
+
+ var return_dic : Dictionary = dict["return"]
+ var return_type : String = "void"
+ var return_value : String = "pass"
+ if !return_dic["class_name"].is_empty():
+ return_type = (return_dic["class_name"] as String)
+ return_value = "return null"
+ else:
+ var _type : int = return_dic["type"]
+ if _type < 1:
+ var func_name : String = str(dict["name"]).to_lower()
+ if func_name == "get" or __is_variant(func_name):
+ return_type = "Variant"
+ return_value = "return null"
+ else:
+ return_type = "void"
+ else:
+ return_type = IDE.get_type(return_dic["type"])
+ if _type == TYPE_INT:
+ return_value = "return 0"
+ elif _type == TYPE_BOOL:
+ return_value = "return false"
+ elif _type == TYPE_FLOAT:
+ return_value = "return 0.0"
+ elif _type == TYPE_STRING:
+ return_value = 'return ""'
+ elif _type == TYPE_ARRAY:
+ return_value = "return []"
+ else:
+ return_value = str("return ", return_type,"()")
+ return "func {0}({1}) -> {2}:\n\t#TODO: code here :)\n\t{3}".format([dict["name"], params, return_type, return_value])
+
+
+
+static func _get_header_virtual(dict : Dictionary, include_paremeters : bool = true) -> String:
+ var params : String = ""
+ var separator : String = ""
+ if dict.has("args"):
+ var args : Array = dict["args"]
+ var default_args : Array = dict["default_args"]
+ var _default_index : int = default_args.size()
+
+ for y : int in range(args.size() - 1, -1, -1):
+ var arg : Dictionary = args[y]
+ var txt : String = "" #arg["name"]
+ if !(arg["class_name"]).is_empty():
+ txt += str(arg["class_name"] as String)
+ else:
+ var _typeof : int = arg["type"]
+ txt += str(IDE.get_type(_typeof))
+ if include_paremeters and _default_index > 0:
+ _default_index -= 1
+ var def : Variant = default_args[_default_index]
+ var _type : int = typeof(def)
+ if def == null or _type < 1:
+ txt += str(' = null')
+ elif _type < 5:
+ if def is String:
+ txt += str(' = "', def, '"')
+ elif def is StringName:
+ txt += str(' = &"', def, '"')
+ else:
+ txt += str(" = ", def)
+ else:
+ txt += str(" = ",IDE.get_type(typeof(def)), def)
+ params = str(txt, separator, params)
+ separator = ", "
+
+ if dict.has("return"):
+ var return_dic : Dictionary = dict["return"]
+ var return_type : String = "void"
+
+ if !return_dic["class_name"].is_empty():
+ return_type = (return_dic["class_name"] as String)
+ else:
+ var _type : int = return_dic["type"]
+ if _type < 1:
+ var func_name : String = str(dict["name"]).to_lower()
+ if func_name == "get" or __is_variant(func_name):
+ return_type = "Variant"
+ else:
+ return_type = "void"
+ else:
+ return_type = get_type(return_dic["type"])
+
+ #if params.is_empty():
+ #params = "-"
+ return "{0}||{1}||{2}".format([dict["name"], params, return_type]) #Replace x more space.
+ elif dict.has("class_name"):
+ var classname : String = dict["class_name"]
+ if classname.is_empty() and dict.has("type"):
+ classname = get_type(dict["type"])
+ params = classname
+ return "{0}||{1}||void".format([dict["name"], params])
+
+static func __is_variant(func_name : String) -> bool:
+ const FUNC_GET : Array[String] = ["get_", "_get"]
+ for x : String in FUNC_GET:
+ if func_name.begins_with(x) or func_name.ends_with(x):
+ return true
+ return func_name.contains("_get_")
+
+static func get_name_script(script : Script, ref_data : Dictionary = {}) -> StringName:
+ var base_name : StringName = script.get_global_name()
+ if base_name.is_empty():
+ var path : String = script.resource_name
+ if path.is_empty():
+ path = script.resource_path
+ if !path.is_empty():
+ var _name : String = path.get_file()
+ _name = _name.trim_suffix("." + _name.get_extension())
+ base_name = _name
+ else:
+ base_name = &"CustomScript"
+ ref_data["custom"] = true
+ else:
+ base_name = path
+ return base_name
+
+static func _reset() -> void:
+ for x : String in _ref.keys():
+ var object : Variant = _ref[x]
+ if is_instance_valid(object) and object is Node:
+ if _safe_ref.has(x):
+ var parent : Node = _safe_ref[x]
+ if is_instance_valid(parent) and !parent.is_queued_for_deletion():
+ var current : Node = object.get_parent()
+ if current != parent:
+ if is_instance_valid(current):
+ object.reparent(parent)
+ else:
+ parent.add_child(object)
+
+static func _get_reference(container_name : String, root_container : Node, pattern : String, type : String, index : int) -> Variant:
+ if !is_instance_valid(root_container):
+ if debug:
+ # If you recieved this message, try reset the engine with this addon enabled.
+ # | If the problem persist, make a issue on "https://github.com/CodeNameTwister/godot_ide"
+ push_warning("Caution!, not root reference setted!!")
+ return null
+ elif _ref.has(container_name):
+ var object : Variant = _ref[container_name]
+ if is_instance_valid(object):
+ if object is Node:
+ if !_safe_ref.has(container_name):
+ _safe_ref[container_name] = object.get_parent()
+ return object
+ var new_object : Variant = _find(root_container, pattern, type, index)
+ if is_instance_valid(new_object):
+ _ref[container_name] = new_object
+ return new_object
+ if debug:
+ # If you recieved this message, try reset the engine with this addon enabled.
+ # | If the problem persist, make a issue on "https://github.com/CodeNameTwister/godot_ide"
+ push_warning("Caution!, can not found: {0}!!".format([container_name]))
+ return null
+
+static func _find(root : Node, pattern : String, type : String, index : int = 0) -> Node:
+ var e : Array[Node] = root.find_children(pattern, type, true, false)
+ if e.size() > index:
+ return e[index]
+ return null
+
+#endregion
diff --git a/addons/_Godot-IDE_/IDE.gd.uid b/addons/_Godot-IDE_/IDE.gd.uid
new file mode 100644
index 0000000..0cf2c73
--- /dev/null
+++ b/addons/_Godot-IDE_/IDE.gd.uid
@@ -0,0 +1 @@
+uid://d270rwyekls6t
diff --git a/addons/_Godot-IDE_/icon.svg b/addons/_Godot-IDE_/icon.svg
new file mode 100644
index 0000000..5e672c5
--- /dev/null
+++ b/addons/_Godot-IDE_/icon.svg
@@ -0,0 +1,128 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/icon.svg.import b/addons/_Godot-IDE_/icon.svg.import
new file mode 100644
index 0000000..33b9eb0
--- /dev/null
+++ b/addons/_Godot-IDE_/icon.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://brfrp6r0fiwl2"
+path="res://.godot/imported/icon.svg-b579c8a7bc6a03fedff183ec85fa8cc5.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/icon.svg"
+dest_files=["res://.godot/imported/icon.svg-b579c8a7bc6a03fedff183ec85fa8cc5.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugin.cfg b/addons/_Godot-IDE_/plugin.cfg
new file mode 100644
index 0000000..a6cc127
--- /dev/null
+++ b/addons/_Godot-IDE_/plugin.cfg
@@ -0,0 +1,8 @@
+[plugin]
+
+name="Godot-IDE"
+description="GODOT-IDE"
+author="Twister"
+version="0.5.5"
+script="plugin.gd"
+github="https://github.com/CodeNameTwister/Godot-IDE"
diff --git a/addons/_Godot-IDE_/plugin.gd b/addons/_Godot-IDE_/plugin.gd
new file mode 100644
index 0000000..bf2e347
--- /dev/null
+++ b/addons/_Godot-IDE_/plugin.gd
@@ -0,0 +1,299 @@
+@tool
+extends IDE
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# ./plugins: Folder for extensions.
+# ./shared_resources: Free use for any purposes.
+# =============================================================================
+
+var _plugins : Array[EditorPlugin] = []
+var _enable_plugins : Dictionary = {}
+
+func _ready() -> void:
+ debug = false
+
+ main_screen_changed.connect(_on_main_screen_changed)
+ resource_saved.connect(_on_resource_saved)
+ scene_closed.connect(_on_scene_closed)
+ scene_changed.connect(_on_scene_changed)
+ scene_saved.connect(_on_scene_saved)
+
+ _initialize()
+
+func _on_resource_saved(resource : Resource) -> void:
+ for x : EditorPlugin in _plugins:
+ x.resource_saved.emit(resource)
+
+func _on_scene_closed(filepath: String) -> void:
+ for x : EditorPlugin in _plugins:
+ x.scene_closed.emit(filepath)
+
+func _on_scene_changed(scene_root: Node) -> void:
+ for x : EditorPlugin in _plugins:
+ x.scene_changed.emit(scene_root)
+
+func _on_scene_saved(filepath: String) -> void:
+ for x : EditorPlugin in _plugins:
+ x.scene_saved.emit(filepath)
+
+func _on_main_screen_changed(screen_name : String) -> void:
+ for x : EditorPlugin in _plugins:
+ x.main_screen_changed.emit(screen_name)
+
+func _enter_tree() -> void:
+ set_process(true)
+
+func _process(_delta: float) -> void:
+ var editor : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if !editor:
+ return
+ if editor.is_scanning():
+ return
+ set_process(false)
+
+ #Self-Secure All Loaded
+ _initialize()
+
+ var base : String = get_script().resource_path.get_base_dir()
+ var path : String = base.path_join("plugins")
+
+ _init_config(1)
+ _load_plugins(path)
+
+ _sugar_godot(path)
+ _sugar_godot(base.path_join("shared_resources"), "teal")
+
+func _init_config(init : int) -> void:
+ const PATH : String = "res://.godot_ide/"
+ var cfg_path : String = PATH.path_join("config.ini")
+ if !DirAccess.dir_exists_absolute(PATH):
+ DirAccess.make_dir_absolute(PATH)
+ var file : FileAccess = FileAccess.open(PATH.path_join(".gdignore"), FileAccess.WRITE)
+ if file:
+ file.store_string("IDE CONFIG")
+ file.close()
+
+ if init == 1:
+ if FileAccess.file_exists(cfg_path):
+ var cfg : ConfigFile = ConfigFile.new()
+ if cfg.load(cfg_path) == OK:
+ var value : Variant = cfg.get_value("config", "plugin", {})
+ if value is Dictionary:
+ _enable_plugins = value
+ else:
+ var cfg : ConfigFile = ConfigFile.new()
+ cfg.set_value("config", "plugin", _enable_plugins)
+ if cfg.save(cfg_path) != OK:
+ push_warning("Can not save plugin changes!")
+
+ #if Engine.get_version_info().minor > 4:
+ #_enable_plugins["res://addons/_Godot-IDE_/plugins/script_spliter/plugin.gd"] = false
+
+#region __PRX__
+func _apply_changes() -> void:
+ _callback(&"_apply_changes")
+
+func _set_window_layout(configuration: ConfigFile) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_set_window_layout"):
+ plugin.call(&"_set_window_layout", configuration)
+
+func _build() -> bool:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_build"):
+ if !plugin.call(&"_build"):
+ return false
+ return true
+
+func _clear() -> void:
+ _callback(&"_build")
+
+func _disable_plugin() -> void:
+ _callback(&"_disable_plugin")
+
+func _edit(object: Object) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_edit"):
+ plugin.call(&"_edit", object)
+
+func _forward_3d_draw_over_viewport(viewport_control: Control) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_forward_3d_draw_over_viewport"):
+ plugin.call(&"_forward_3d_draw_over_viewport", viewport_control)
+
+func _forward_3d_force_draw_over_viewport(viewport_control: Control) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_forward_3d_force_draw_over_viewport"):
+ plugin.call(&"_forward_3d_force_draw_over_viewport", viewport_control)
+
+func _forward_canvas_draw_over_viewport(viewport_control: Control) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_forward_canvas_draw_over_viewport"):
+ plugin.call(&"_forward_canvas_draw_over_viewport", viewport_control)
+
+func _forward_canvas_force_draw_over_viewport(viewport_control: Control) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_forward_canvas_force_draw_over_viewport"):
+ plugin.call(&"_forward_canvas_force_draw_over_viewport", viewport_control)
+
+func _forward_canvas_gui_input(event: InputEvent) -> bool:
+ var out : bool = false
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(&"_forward_canvas_gui_input"):
+ out = plugin.call(&"_forward_canvas_gui_input", event) or out
+ return out
+#endregion
+
+func _callback(method : StringName) -> void:
+ for plugin : EditorPlugin in _plugins:
+ if plugin.has_method(method):
+ plugin.call(method)
+
+func _exit_tree() -> void:
+ for x : Node in _plugins:
+ if is_instance_valid(x):
+ if !x.is_queued_for_deletion():
+ x.queue_free()
+
+ _plugins.clear()
+ _init_config(0)
+
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ if editor.settings_changed.is_connected(_on_changes):
+ editor.settings_changed.disconnect(_on_changes)
+
+ if is_instance_valid(IDE._menu):
+ IDE._menu.queue_free()
+ IDE._menu = null
+
+func _load_plugins(path : String) -> void:
+ if !is_instance_valid(IDE._menu):
+ var file : MenuButton = get_file_menu_button()
+ if is_instance_valid(file):
+ var root : Node = file.get_parent()
+ IDE._menu = MenuButton.new()
+ IDE._menu.text = "Godot-IDE"
+ root.add_child(_menu)
+ root.move_child(_menu, mini(1, root.get_child_count() - 1))
+
+ if !DirAccess.dir_exists_absolute(path):
+ path = path.get_base_dir().get_file()
+ if EditorInterface.is_plugin_enabled(path):
+ EditorInterface.set_plugin_enabled(path, false)
+ printerr("{0}: Error, can not find 'plugins' folder! [0x00000003]".format([path.capitalize().to_upper()]))
+ return
+
+ var dir :DirAccess = DirAccess.open(path)
+ var plugins_dir : Array = []
+ var plugins_file : Array = []
+ var authors : PackedStringArray = []
+
+ if dir:
+ dir.list_dir_begin()
+ var file_name : String = dir.get_next()
+ while !file_name.is_empty():
+ if dir.current_is_dir():
+ plugins_dir.append(path.path_join(file_name))
+ file_name = dir.get_next()
+ dir.list_dir_end()
+ else:
+ printerr("{0}:error, can not open 'plugins' folder! [0x00000005]".format([path.get_base_dir().get_file().capitalize().to_upper()]))
+
+ while plugins_dir.size() > 0:
+ var current_path : String = plugins_dir.pop_back()
+ var plugin_path : String = current_path.path_join("plugin.gd")
+ var plugin_cfg : String = current_path.path_join("plugin.cfg")
+
+ if FileAccess.file_exists(plugin_cfg):
+ var cfg : ConfigFile = ConfigFile.new()
+ if cfg.load(plugin_cfg) == OK:
+ if cfg.has_section_key("plugin", "script"):
+ plugin_path = current_path.path_join(str(cfg.get_value("plugin", "script")))
+ if cfg.has_section_key("plugin", "author"):
+ var value : String = str(cfg.get_value("plugin", "author"))
+ if !value.is_empty() and !authors.has(value):
+ authors.append(value)
+
+ if !FileAccess.file_exists(plugin_path):
+ plugin_path = current_path.path_join(current_path.get_file())
+ if !FileAccess.file_exists(plugin_path):
+ printerr("{0}:error, can not open 'plugin/{1}' folder! [0x00000005]".format([path.get_base_dir().get_file().capitalize().to_upper(), current_path.get_file()]))
+ continue
+ plugins_file.append(plugin_path)
+
+ var current_plugins : Dictionary = {}
+ for plugin : String in plugins_file:
+ if _enable_plugins.has(plugin):
+ if _enable_plugins[plugin] == false:
+ continue
+ var variant : Variant = ResourceLoader.load(plugin)
+ if variant is Script:
+ if variant.can_instantiate():
+ if !variant.is_tool():
+ push_warning("Plugin script is not tool: {0}".format([plugin]))
+ variant = variant.new()
+ if variant is EditorPlugin:
+ _plugins.append(variant)
+ current_plugins[plugin] = true
+
+ _enable_plugins = current_plugins
+
+ for plugin : EditorPlugin in _plugins:
+ get_parent().add_child(plugin)
+ if plugin.has_method(&"_enable_plugin"):
+ plugin.call(&"_enable_plugin")
+
+ print("[Godot-IDE Extension]")
+ if authors.size() > 0:
+ print("> Plugin Contributors: {0}".format([", ".join(authors)]))
+
+
+func _sugar_godot(dir : String, col : String = "blue") -> void:
+ if !ProjectSettings.has_setting("file_customization/folder_colors"):
+ ProjectSettings.set_setting("file_customization/folder_colors", {dir: col})
+ else:
+ if !dir.ends_with("/"):
+ dir += "/"
+ var data : Dictionary = ProjectSettings.get_setting("file_customization/folder_colors", {})
+ if !data.has(dir):
+ data[dir] = col
+ ProjectSettings.set_setting("file_customization/folder_colors", data)
+ else:
+ return
+ var editor : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if editor:
+ editor.scan.call_deferred()
+
+func _on_changes() -> void:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ var changes : PackedStringArray = editor.get_changed_settings()
+ if "plugin/gd_override_functions/inheritance/virtual_functions_begins_with" in changes:
+ IDE.VIRTUAL_METHODS = editor.get_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with")
+ if "plugin/gd_override_functions/inheritance/private_functions_begins_with" in changes:
+ IDE.PRIVATE_METHODS = editor.get_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with")
+
+func _initialize() -> void:
+ var dirt : Dictionary = {}
+ var dat : Array[Dictionary] = (get_script() as Script).get_script_method_list()
+ for dct : Dictionary in dat:
+ var key : String = dct["name"]
+ if dirt.has(key):
+ continue
+ dirt[key] = true
+ if has_method(key):
+ if key.begins_with("get_"):
+ if !dct.has("args") or dct["args"].size() == 0:
+ call(key)
+
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ if editor.has_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with"):
+ IDE.VIRTUAL_METHODS = editor.get_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with")
+ if editor.has_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with"):
+ IDE.PRIVATE_METHODS = editor.get_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with")
+ if !editor.settings_changed.is_connected(_on_changes):
+ editor.settings_changed.connect(_on_changes)
diff --git a/addons/_Godot-IDE_/plugin.gd.uid b/addons/_Godot-IDE_/plugin.gd.uid
new file mode 100644
index 0000000..e12e430
--- /dev/null
+++ b/addons/_Godot-IDE_/plugin.gd.uid
@@ -0,0 +1 @@
+uid://bqmc0vi754ifc
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/assets/flatbox.tres b/addons/_Godot-IDE_/plugins/fancy_filters_script/assets/flatbox.tres
new file mode 100644
index 0000000..50fd0c6
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/assets/flatbox.tres
@@ -0,0 +1,5 @@
+[gd_resource type="StyleBoxEmpty" format=3 uid="uid://dwpkbhgi7dk8"]
+
+[resource]
+content_margin_left = 8.0
+content_margin_right = 8.0
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd
new file mode 100644
index 0000000..93e90a8
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd
@@ -0,0 +1,14 @@
+@tool
+extends Button
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+
+func _pressed() -> void:
+ if owner:
+ if owner.has_method(name):
+ owner.call(name)
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd.uid
new file mode 100644
index 0000000..e66c99b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd.uid
@@ -0,0 +1 @@
+uid://b1v225muollg5
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd
new file mode 100644
index 0000000..2b1b0ae
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd
@@ -0,0 +1,35 @@
+@tool
+extends Button
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+func update_settings() -> void:
+ var variant : Variant = owner.get(name)
+ if variant is bool:
+ button_pressed = variant
+
+ visual()
+
+
+func _ready() -> void:
+ update_settings()
+
+func _pressed() -> void:
+ if owner.has_method(&"enable_filter"):
+ owner.call(&"enable_filter", name, button_pressed)
+ visual()
+
+func visual() -> void:
+ if button_pressed:
+ var value : Variant = owner.get(str(name, "_color"))
+ if value is Color:
+ modulate = value
+ else:
+ modulate = Color.WHITE
+ else:
+ modulate = Color.DARK_GRAY
+ modulate.a = 0.7
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd.uid
new file mode 100644
index 0000000..61dcdff
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd.uid
@@ -0,0 +1 @@
+uid://gct2x12uf8jj
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd
new file mode 100644
index 0000000..4c79341
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd
@@ -0,0 +1,36 @@
+@tool
+extends Control
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+@export var container : Control = null
+
+func _ready() -> void:
+ var p : Node = get_parent()
+ if !p.child_entered_tree.is_connected(_on_child):
+ p.child_entered_tree.connect(_on_child)
+
+ visibility_changed.connect(_on_visible)
+
+func _on_child(n : Node) -> void:
+ var p : Node = get_parent()
+ if is_instance_valid(p) and !p.is_queued_for_deletion():
+ if p.get_child_count() > 0:
+ p.move_child.call_deferred(self, p.get_child_count() - 1)
+
+func _on_visible() -> void:
+ if visible:
+ var packed : PackedScene = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.tscn")
+ if packed:
+ var c : Control = packed.instantiate()
+ container.add_child(c)
+ c.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ c.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ else:
+ for x : Node in container.get_children():
+ x.queue_free()
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd.uid
new file mode 100644
index 0000000..928d051
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd.uid
@@ -0,0 +1 @@
+uid://cbvs583dwh5o
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd
new file mode 100644
index 0000000..671a9c5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd
@@ -0,0 +1,142 @@
+@tool
+extends Control
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+@export var _type_members : TabContainer
+@export var _accessibility : TabContainer
+
+@export var sorty_name_enabled : CheckBox
+
+@export var order_name_check : CheckBox
+@export var order_name_button : Button
+
+@export var background_color : Button
+@export var use_dots : Button
+@export var flat_mode_button : Button
+@export var separate_script_list_button : Button
+
+const NORMAL_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/up.svg")
+const INVERT_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/down.svg")
+
+signal on_update_order(data : Dictionary)
+
+enum TYPE_ORDER{
+ NONE = 0,
+ NORMAL = 1,
+ INVERT = 2
+}
+
+var name_order : TYPE_ORDER = TYPE_ORDER.NORMAL
+
+func use_background_color_in_script_info() -> void:
+ IDE.set_config("fancy_filters_script", "use_background_color_in_script_info", background_color.button_pressed)
+
+func use_dots_as_item_icons() -> void:
+ IDE.set_config("fancy_filters_script", "use_dots_as_item_icons", use_dots.button_pressed)
+
+func flat_mode() -> void:
+ IDE.set_config("fancy_filters_script", "flat_mode", flat_mode_button.button_pressed)
+
+func separate_script_list() -> void:
+ IDE.set_config("fancy_filters_script", "separate_container_list", separate_script_list_button.button_pressed)
+
+func update_settings() -> void:
+ var order : Variant = IDE.get_config("fancy_filters_script", "members_order_by")
+ var name_type : Variant = IDE.get_config("fancy_filters_script", "name_order_by")
+ var background_pressed : Variant = IDE.get_config("fancy_filters_script", "use_background_color_in_script_info")
+ var use_dots_pressed: Variant = IDE.get_config("fancy_filters_script", "use_dots_as_item_icons")
+ var flat_mode_pressed : Variant = IDE.get_config("fancy_filters_script", "flat_mode")
+ var separate_script_list_pressed : Variant = IDE.get_config("fancy_filters_script", "separate_container_list")
+
+ if !(separate_script_list_pressed is bool):
+ separate_script_list_pressed = false
+ if !(order is Array):
+ order = []
+ if !(name_type is int):
+ name_type = 0
+ if !(background_pressed is bool):
+ background_pressed = false
+ if !(use_dots_pressed is bool):
+ use_dots_pressed = false
+ if !(flat_mode_pressed is bool):
+ flat_mode_pressed = false
+
+ use_dots.button_pressed = use_dots_pressed
+ background_color.button_pressed = background_pressed
+ flat_mode_button.button_pressed = flat_mode_pressed
+ separate_script_list_button.button_pressed = separate_script_list_pressed
+
+ name_order = name_type
+
+ order_name_check.button_pressed = name_order != 0
+
+ if name_order == TYPE_ORDER.INVERT:
+ order_name_button.icon = INVERT_ICON
+ else:
+ order_name_button.icon = NORMAL_ICON
+
+ for x : Node in _type_members.get_children():
+ match x.name:
+ &"Properties":
+ for z : int in range(order.size()):
+ if order[z] == 0:
+ _type_members.move_child(x, z)
+ &"Methods":
+ for z : int in range(order.size()):
+ if order[z] == 1:
+ _type_members.move_child(x, z)
+ &"Signals":
+ for z : int in range(order.size()):
+ if order[z] == 2:
+ _type_members.move_child(x, z)
+ &"Constant":
+ for z : int in range(order.size()):
+ if order[z] == 3:
+ _type_members.move_child(x, z)
+ order_name_button.disabled = !order_name_check.button_pressed
+
+func _ready() -> void:
+ update_settings()
+
+func order_name_check_button() -> void:
+ order_name_button.disabled = !order_name_check.button_pressed
+ if order_name_check.button_pressed == false:
+ IDE.set_config("fancy_filters_script", "name_order_by", 0)
+ else:
+ if order_name_button.icon == INVERT_ICON:
+ name_order = TYPE_ORDER.INVERT
+ else:
+ name_order = TYPE_ORDER.NORMAL
+ IDE.set_config("fancy_filters_script", "name_order_by", name_order)
+
+func order_name() -> void:
+ if name_order == TYPE_ORDER.NORMAL:
+ name_order = TYPE_ORDER.INVERT
+ order_name_button.icon = INVERT_ICON
+ else:
+ name_order = TYPE_ORDER.NORMAL
+ order_name_button.icon = NORMAL_ICON
+ if order_name_check.button_pressed == false:
+ IDE.set_config("fancy_filters_script", "name_order_by", 0)
+ else:
+ IDE.set_config("fancy_filters_script", "name_order_by", name_order)
+
+func set_settings() -> void:
+ var new_order : Array[int] = []
+
+ for x : Node in _type_members.get_children():
+ match x.name:
+ &"Properties":
+ new_order.append(0)
+ &"Methods":
+ new_order.append(1)
+ &"Signals":
+ new_order.append(2)
+ &"Constant":
+ new_order.append(3)
+
+ IDE.set_config("fancy_filters_script", "members_order_by", new_order)
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd.uid
new file mode 100644
index 0000000..d69ce3c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd.uid
@@ -0,0 +1 @@
+uid://blc70goak5ixm
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.tscn b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.tscn
new file mode 100644
index 0000000..2928b54
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.tscn
@@ -0,0 +1,158 @@
+[gd_scene load_steps=6 format=3 uid="uid://bhrieo2dhroxj"]
+
+[ext_resource type="Script" uid="uid://blc70goak5ixm" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/components/settings.gd" id="1_8gu0h"]
+[ext_resource type="StyleBox" uid="uid://dwpkbhgi7dk8" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/assets/flatbox.tres" id="1_e4fgx"]
+[ext_resource type="Texture2D" uid="uid://bgjo43cuidob1" path="res://addons/_Godot-IDE_/shared_resources/up.svg" id="3_f6f5j"]
+[ext_resource type="Script" uid="uid://drngb2pss3dtj" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd" id="3_g0drt"]
+[ext_resource type="Script" uid="uid://b1v225muollg5" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd" id="4_f6f5j"]
+
+[node name="Settings" type="VBoxContainer" node_paths=PackedStringArray("_type_members", "_accessibility", "sorty_name_enabled", "order_name_check", "order_name_button", "background_color", "use_dots", "flat_mode_button", "separate_script_list_button")]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_8gu0h")
+_type_members = NodePath("Type")
+_accessibility = NodePath("Accessibility")
+sorty_name_enabled = NodePath("order_name_check_button")
+order_name_check = NodePath("order_name_check_button")
+order_name_button = NodePath("order_name")
+background_color = NodePath("use_background_color_in_script_info")
+use_dots = NodePath("use_dots_as_item_icons")
+flat_mode_button = NodePath("flat_mode")
+separate_script_list_button = NodePath("separate_script_list")
+
+[node name="order_name_check_button" type="CheckBox" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+button_pressed = true
+text = "Enable Order By Name"
+script = ExtResource("4_f6f5j")
+
+[node name="order_name" type="Button" parent="."]
+custom_minimum_size = Vector2(200, 0)
+layout_mode = 2
+size_flags_horizontal = 3
+tooltip_text = "Make sorty by using the name member."
+text = "Sort Name"
+icon = ExtResource("3_f6f5j")
+icon_alignment = 2
+script = ExtResource("4_f6f5j")
+
+[node name="Sep3" type="HSeparator" parent="."]
+layout_mode = 2
+
+[node name="Label6" type="Label" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+text = "Order By Members (Drag and Drop)"
+
+[node name="Type" type="TabContainer" parent="."]
+layout_mode = 2
+size_flags_horizontal = 3
+tooltip_text = "Change order by drag values."
+theme_override_styles/panel = ExtResource("1_e4fgx")
+theme_override_styles/tabbar_background = ExtResource("1_e4fgx")
+theme_override_styles/tab_unselected = ExtResource("1_e4fgx")
+theme_override_styles/tab_hovered = ExtResource("1_e4fgx")
+theme_override_styles/tab_selected = ExtResource("1_e4fgx")
+theme_override_styles/tab_disabled = ExtResource("1_e4fgx")
+theme_override_styles/tab_focus = ExtResource("1_e4fgx")
+current_tab = 0
+drag_to_rearrange_enabled = true
+script = ExtResource("3_g0drt")
+
+[node name="Properties" type="Control" parent="Type"]
+layout_mode = 2
+metadata/_tab_index = 0
+
+[node name="Methods" type="Control" parent="Type"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 1
+
+[node name="Signals" type="Control" parent="Type"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 2
+
+[node name="Constant" type="Control" parent="Type"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 3
+
+[node name="Sep" type="HSeparator" parent="."]
+visible = false
+layout_mode = 2
+
+[node name="accessibility2" type="Label" parent="."]
+visible = false
+layout_mode = 2
+text = "Order By Accesibility (Drag and Drop)"
+
+[node name="Accessibility" type="TabContainer" parent="."]
+visible = false
+layout_mode = 2
+size_flags_horizontal = 3
+theme_override_styles/panel = ExtResource("1_e4fgx")
+theme_override_styles/tabbar_background = ExtResource("1_e4fgx")
+theme_override_styles/tab_unselected = ExtResource("1_e4fgx")
+theme_override_styles/tab_hovered = ExtResource("1_e4fgx")
+theme_override_styles/tab_selected = ExtResource("1_e4fgx")
+theme_override_styles/tab_disabled = ExtResource("1_e4fgx")
+theme_override_styles/tab_focus = ExtResource("1_e4fgx")
+current_tab = 0
+drag_to_rearrange_enabled = true
+script = ExtResource("3_g0drt")
+
+[node name="Export" type="Control" parent="Accessibility"]
+layout_mode = 2
+metadata/_tab_index = 0
+
+[node name="Static" type="Control" parent="Accessibility"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 1
+
+[node name="Public" type="Control" parent="Accessibility"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 2
+
+[node name="Virtual" type="Control" parent="Accessibility"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 3
+
+[node name="Private" type="Control" parent="Accessibility"]
+visible = false
+layout_mode = 2
+metadata/_tab_index = 4
+
+[node name="Sep2" type="HSeparator" parent="."]
+layout_mode = 2
+
+[node name="use_dots_as_item_icons" type="CheckBox" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+text = "Use Dots As Script Info Items Icons"
+script = ExtResource("4_f6f5j")
+
+[node name="use_background_color_in_script_info" type="CheckBox" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+text = "Use Background Colors in Script Info Items"
+script = ExtResource("4_f6f5j")
+
+[node name="flat_mode" type="CheckBox" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+text = "Flat Mode"
+script = ExtResource("4_f6f5j")
+
+[node name="separate_script_list" type="CheckBox" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+text = "Separate Script List"
+script = ExtResource("4_f6f5j")
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd
new file mode 100644
index 0000000..dbaae6b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd
@@ -0,0 +1,13 @@
+@tool
+extends TabContainer
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_DRAG_END:
+ if owner.has_method(&"set_settings"):
+ owner.call(&"set_settings")
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd.uid
new file mode 100644
index 0000000..7b8db2c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/components/tab_container.gd.uid
@@ -0,0 +1 @@
+uid://drngb2pss3dtj
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/filter_scene.tscn b/addons/_Godot-IDE_/plugins/fancy_filters_script/filter_scene.tscn
new file mode 100644
index 0000000..ec049ab
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/filter_scene.tscn
@@ -0,0 +1,179 @@
+[gd_scene load_steps=14 format=3 uid="uid://dhlxe0qj82e8y"]
+
+[ext_resource type="Script" uid="uid://bwtq1pkujrb1d" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd" id="1_5gq01"]
+[ext_resource type="Script" uid="uid://gct2x12uf8jj" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/button_filter.gd" id="2_djmkv"]
+[ext_resource type="Script" uid="uid://dpkvcqsih4i8w" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd" id="2_u1yuj"]
+[ext_resource type="Texture2D" uid="uid://cjpyaeqxjujsy" path="res://addons/_Godot-IDE_/shared_resources/MethodOverride.svg" id="3_7xcf1"]
+[ext_resource type="Texture2D" uid="uid://dqfn3tghaer0y" path="res://addons/_Godot-IDE_/shared_resources/ScriptExtend.svg" id="3_nynvq"]
+[ext_resource type="Texture2D" uid="uid://ouml4wypk5bq" path="res://addons/_Godot-IDE_/shared_resources/MemberConstant.svg" id="4_7xcf1"]
+[ext_resource type="Texture2D" uid="uid://bu2y52as0gijo" path="res://addons/_Godot-IDE_/shared_resources/MemberSignal.svg" id="5_2xgjc"]
+[ext_resource type="Texture2D" uid="uid://cec6jsyssk4ys" path="res://addons/_Godot-IDE_/shared_resources/MemberProperty.svg" id="5_nynvq"]
+[ext_resource type="Texture2D" uid="uid://cgdceyfng83st" path="res://addons/_Godot-IDE_/shared_resources/MemberMethod.svg" id="6_ndpn6"]
+[ext_resource type="Texture2D" uid="uid://bjmtfc58y1sbs" path="res://addons/_Godot-IDE_/shared_resources/InterfaceScript.svg" id="8_goajo"]
+[ext_resource type="Texture2D" uid="uid://816fejewtbfj" path="res://addons/_Godot-IDE_/shared_resources/search.svg" id="10_2xgjc"]
+[ext_resource type="Script" uid="uid://bbbdyhh8lhw4a" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd" id="11_1yamo"]
+[ext_resource type="Script" uid="uid://cbvs583dwh5o" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/components/order_settings.gd" id="12_u1yuj"]
+
+[node name="Container" type="TabContainer" node_paths=PackedStringArray("button_container", "tree_container")]
+custom_minimum_size = Vector2(64, 64)
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+current_tab = 0
+script = ExtResource("1_5gq01")
+button_container = NodePath("Script Info/VContainer/SContainer/SHContainer")
+tree_container = NodePath("Script Info/SContainer/Tree")
+
+[node name="Script Info" type="VBoxContainer" parent="."]
+layout_mode = 2
+metadata/_tab_index = 0
+
+[node name="VContainer" type="VBoxContainer" parent="Script Info"]
+layout_mode = 2
+
+[node name="SContainer" type="ScrollContainer" parent="Script Info/VContainer"]
+custom_minimum_size = Vector2(0, 38)
+layout_mode = 2
+size_flags_vertical = 3
+horizontal_scroll_mode = 3
+vertical_scroll_mode = 3
+script = ExtResource("2_u1yuj")
+
+[node name="SHContainer" type="HBoxContainer" parent="Script Info/VContainer/SContainer"]
+custom_minimum_size = Vector2(0, 32)
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="show_all" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+toggle_mode = true
+button_pressed = true
+text = "All"
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_inheritance" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+modulate = Color(0.960784, 0.960784, 0.960784, 1)
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show inheritance"
+toggle_mode = true
+icon = ExtResource("3_7xcf1")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_functions" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show Properties"
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("6_ndpn6")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_properties" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+modulate = Color(0.7372549, 0.8784314, 1, 1)
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show Properties"
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("5_nynvq")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_signals" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+modulate = Color(0.89998645, 0.817927, 1, 1)
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show Signals"
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("5_2xgjc")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_constants" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+modulate = Color(0, 1, 1, 1)
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show Constants"
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("4_7xcf1")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_parent_class" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+modulate = Color(0.4627451, 0.8117647, 0.70980394, 1)
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show parent classes"
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("3_nynvq")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="show_native_class" type="Button" parent="Script Info/VContainer/SContainer/SHContainer"]
+modulate = Color(0.4627451, 0.8117647, 0.70980394, 1)
+custom_minimum_size = Vector2(32, 0)
+layout_mode = 2
+tooltip_text = "Show native classes"
+toggle_mode = true
+icon = ExtResource("8_goajo")
+icon_alignment = 1
+script = ExtResource("2_djmkv")
+
+[node name="LineEdit" type="LineEdit" parent="Script Info/VContainer" node_paths=PackedStringArray("tree")]
+layout_mode = 2
+placeholder_text = "Search Bar"
+clear_button_enabled = true
+right_icon = ExtResource("10_2xgjc")
+script = ExtResource("11_1yamo")
+tree = NodePath("../../SContainer/Tree")
+
+[node name="SContainer" type="ScrollContainer" parent="Script Info"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="Tree" type="Tree" parent="Script Info/SContainer"]
+auto_translate_mode = 2
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme_override_constants/h_separation = 0
+theme_override_constants/item_margin = 6
+columns = 2
+allow_rmb_select = true
+
+[node name="Settings" type="VBoxContainer" parent="." node_paths=PackedStringArray("container")]
+visible = false
+layout_mode = 2
+script = ExtResource("12_u1yuj")
+container = NodePath("VBoxContainer/MarginContainer")
+metadata/_tab_index = 1
+
+[node name="VBoxContainer" type="VBoxContainer" parent="Settings"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="Label" type="Label" parent="Settings/VBoxContainer"]
+layout_mode = 2
+text = "Filter Settings"
+horizontal_alignment = 1
+
+[node name="HSeparator" type="HSeparator" parent="Settings/VBoxContainer"]
+layout_mode = 2
+
+[node name="MarginContainer" type="ScrollContainer" parent="Settings/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd
new file mode 100644
index 0000000..73c1608
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd
@@ -0,0 +1,22 @@
+@tool
+extends ScrollContainer
+
+func _ready() -> void:
+ mouse_entered.connect(_on_mouse)
+ _out_mouse()
+
+func _process(_delta: float) -> void:
+ if is_inside_tree():
+ if get_global_rect().has_point(get_global_mouse_position()):
+ return
+ _out_mouse()
+
+func _on_mouse() -> void:
+ horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
+ vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
+ set_process(true)
+
+func _out_mouse() -> void:
+ horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER
+ vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER
+ set_process(false)
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd.uid
new file mode 100644
index 0000000..c325a65
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/filters_container.gd.uid
@@ -0,0 +1 @@
+uid://dpkvcqsih4i8w
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd
new file mode 100644
index 0000000..3786f22
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd
@@ -0,0 +1,85 @@
+@tool
+extends LineEdit
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+
+@export var tree : Tree = null
+
+
+func _enter_tree() -> void:
+ if !is_in_group(&"UPDATE_ON_SAVE"):
+ add_to_group(&"UPDATE_ON_SAVE")
+
+func _exit_tree() -> void:
+ if is_in_group(&"UPDATE_ON_SAVE"):
+ remove_from_group(&"UPDATE_ON_SAVE")
+
+func _process(_delta: float) -> void:
+ set_process(false)
+ if text.is_empty():
+ return
+ sxrch.call_deferred(text)
+
+func update() -> void:
+ set_process(true)
+
+func _ready() -> void:
+ set_process(false)
+ text_changed.connect(sxrch)
+
+func reset() -> void:
+ if tree:
+ var root : TreeItem = tree.get_root()
+ if !root:
+ return
+ _reset(root)
+
+func _reset(root : TreeItem) -> void:
+ root.visible = true
+ for c : TreeItem in root.get_children():
+ _reset(c)
+
+func sxrch(txt : String) -> void:
+ reset()
+ if txt.is_empty():
+ return
+ if tree:
+ var root : TreeItem = tree.get_root()
+ if !root:
+ return
+ var rgx0 : RegEx = RegEx.create_from_string("(?i)\\b{0}\\b".format([txt]))
+ var rgx1 : RegEx = RegEx.create_from_string("(?i).*{0}.*".format([txt]))
+ var d0 : Array[TreeItem] = []
+ var d1 : Array[TreeItem] = []
+
+ for x : RegEx in [rgx0, rgx1]:
+ if !is_instance_valid(x) or !x.is_valid():
+ return
+
+ _sxrch(root, rgx0, rgx1, d0, d1)
+ root.visible = true
+
+ for t : TreeItem in d0:
+ _visible(t)
+ for t : TreeItem in d1:
+ _visible(t)
+
+func _visible(root : TreeItem) -> void:
+ if root:
+ root.visible = true
+ _visible(root.get_parent())
+
+func _sxrch(root : TreeItem, rgx0 : RegEx, rgx1 : RegEx, d0 : Array[TreeItem], d1 : Array[TreeItem]) -> void:
+ var txt : String = root.get_text(0)
+ root.visible = false
+ if rgx0.search(txt) != null:
+ d0.append(root)
+ elif d0.size() == 0 and rgx1.search(txt) != null:
+ d1.append(root)
+ for x : TreeItem in root.get_children():
+ _sxrch(x, rgx0, rgx1, d0, d1)
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd.uid
new file mode 100644
index 0000000..1b2b21f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/line_edit.gd.uid
@@ -0,0 +1 @@
+uid://bbbdyhh8lhw4a
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.cfg b/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.cfg
new file mode 100644
index 0000000..5982422
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.cfg
@@ -0,0 +1,8 @@
+[plugin]
+
+name="Fance Editor Filters"
+description="Tool Addon for godot 4."
+author="Twister"
+version="1.1.5"
+github="https://github.com/CodeNameTwister"
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd
new file mode 100644
index 0000000..6c7e8ee
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd
@@ -0,0 +1,272 @@
+@tool
+extends EditorPlugin
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+var TAB : PackedScene = preload("res://addons/_Godot-IDE_/plugins/fancy_filters_script/filter_scene.tscn")
+
+var _parent : Control = null
+var _container : Control = null
+var _script_info : Control = null
+var _placeholder : Control = null
+
+var _id_show_hide_tool : int = -1
+var _id_toggle_position_tool : int = -1
+var _id_switch_panels : int = -1
+
+var _c_input_show_hide : InputEventKey = null
+var _c_input_switch_panels : InputEventKey = null
+
+var _as_separate_container : bool = false
+
+var _input_defined : bool = false
+
+func _on_changes() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings:
+ for x : String in settings.get_changed_settings():
+ if "separate_container_list" in x:
+ var value : Variant = IDE.get_config("fancy_filters_script", "separate_container_list")
+ if value is bool and value != _as_separate_container:
+ _as_separate_container = value
+ _exit_tree()
+ _enter_tree()
+ break
+
+func _init() -> void:
+ var input0 : Variant = IDE.get_config("fancy_filters_script", "show_hide")
+ var input1 : Variant = IDE.get_config("fancy_filters_script", "switch_panels")
+ var as_separate_container : Variant = IDE.get_config("fancy_filters_script", "separate_container_list")
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings:
+ # 0.5.1
+ if settings.has_setting("plugin/godot_ide/fancy_filter_script/script_list_and_filter_to_right"):
+ settings.set_setting("plugin/godot_ide/fancy_filters_script/script_list_and_filter_to_right", settings.get_setting("plugin/godot_ide/fancy_filter_script/script_list_and_filter_to_right"))
+ settings.set_setting("plugin/godot_ide/fancy_filter_script/script_list_and_filter_to_right", null)
+
+ if !settings.settings_changed.is_connected(_on_changes):
+ settings.settings_changed.connect(_on_changes)
+
+ if as_separate_container is bool:
+ _as_separate_container = _as_separate_container
+ else:
+ IDE.set_config("fancy_filters_script", "separate_container_list", _as_separate_container)
+
+ if input0 is InputEventKey:
+ _c_input_show_hide = input0
+ else:
+ _c_input_show_hide = InputEventKey.new()
+ _c_input_show_hide.pressed = true
+ _c_input_show_hide.ctrl_pressed = true
+ _c_input_show_hide.keycode = KEY_T
+ IDE.set_config("fancy_filters_script", "show_hide", _c_input_show_hide)
+
+
+ if input1 is InputEventKey:
+ _c_input_switch_panels = input1
+ else:
+ _c_input_switch_panels = InputEventKey.new()
+ _c_input_switch_panels.pressed = true
+ _c_input_switch_panels.ctrl_pressed = true
+ _c_input_switch_panels.keycode = KEY_G
+ IDE.set_config("fancy_filters_script", "switch_panels", _c_input_switch_panels)
+
+func _get_traduce(msg : String) -> String:
+ return msg
+
+func _on_pop_pressed(index : int) -> void:
+ if index > -1:
+ if index == _id_show_hide_tool:
+ _container.visible = !_container.visible
+ elif index == _id_toggle_position_tool:
+ toggle_position()
+ elif index == _id_switch_panels:
+ if !_script_info.visible:
+ var tab : Variant = _script_info.get_parent()
+ if tab is TabContainer:
+ tab.current_tab = _script_info.get_index()
+ else:
+ var script_list : Control = IDE.get_script_list_container()
+ if is_instance_valid(script_list):#
+ var tab : Variant = script_list.get_parent()
+ if tab is TabContainer:
+ tab.current_tab = script_list.get_index()
+
+func _apply_changes() -> void:
+ if _container:
+ if _container.has_method(&"force_update"):
+ _container.call_deferred(&"force_update")
+ get_tree().call_group(&"UPDATE_ON_SAVE", &"update")
+
+func _enter_tree() -> void:
+ var container : VSplitContainer = IDE.get_script_list_container()
+ if container:
+ var variant : Variant = IDE.get_config("fancy_filters_script", "script_list_and_filter_to_right")
+ var expected_index : int = 0
+ if variant is bool:
+ if variant == true:
+ expected_index = 1
+
+ container.name = "Script List"
+ _container = TAB.instantiate()
+
+ if _container.get_child_count() > 0:
+ _script_info = _container.get_child(0)
+
+ var parent : Control = container.get_parent()
+ _parent = parent
+ if !_as_separate_container:
+ var x : int = container.get_child_count()
+ if x > 1:
+ var cntl : Node = container.get_child(x - 1)
+ if cntl is Control:
+ if _placeholder == null:
+ _placeholder = Control.new()
+ _placeholder.visible = false
+ cntl.reparent(_placeholder)
+
+ container.add_child(_container)
+ container.move_child(_container, 0)
+ container.split_offset = 100
+ container.clamp_split_offset()
+ else:
+ container.add_child(_container)
+ else:
+ parent.add_child(_container)
+ container.reparent(_container)
+
+ toggle_position()
+
+ var cnt : Control = _container
+ if !_as_separate_container:
+ cnt = _container.get_parent()
+
+ if cnt.get_index() != expected_index:
+ toggle_position()
+
+ var menu : MenuButton = IDE.get_menu_button()
+ if is_instance_valid(menu):
+ if _input_defined:
+ return
+
+ _input_defined = true
+
+ var pop : PopupMenu = menu.get_popup()
+ var total : int = pop.item_count
+ var msg : String = _get_traduce("Show/Hide Scripts and Filters Panel")
+
+ if !pop.index_pressed.is_connected(_on_pop_pressed):
+ pop.index_pressed.connect(_on_pop_pressed)
+
+ _add_input(pop, msg, _c_input_show_hide)
+ _id_show_hide_tool = total
+
+ total = pop.item_count
+ msg = _get_traduce("Toggle Script Info/Script List Panel (Only if the separated option enabled)")
+ _add_input(pop, msg, _c_input_switch_panels)
+ _id_switch_panels = total
+
+ total = pop.item_count
+ msg = _get_traduce("Toggle Position Script and Filters Panel")
+ pop.add_item(msg, -1) #, KEY_MASK_CTRL | KEY_NOT_DEFINED_YET
+ _id_toggle_position_tool =total
+
+func _add_input(pop : PopupMenu, msg : String, input : InputEventKey) -> void:
+ if null != input:
+ if input.ctrl_pressed and input.alt_pressed:
+ pop.add_item(msg, -1, KEY_MASK_CTRL | KEY_MASK_ALT | input.keycode)
+ elif input.ctrl_pressed:
+ pop.add_item(msg, -1, KEY_MASK_CTRL | input.keycode)
+ elif input.alt_pressed:
+ pop.add_item(msg, -1, KEY_MASK_ALT | input.keycode)
+ else:
+ pop.add_item(msg, -1, input.keycode)
+ else:
+ pop.add_item(msg, -1, input.keycode) #, KEY_MASK_CTRL | KEY_NOT_DEFINED_YET) #, KEY_MASK_CTRL | KEY_NOT_DEFINED_YET
+
+func _ready() -> void:
+ if !Engine.get_main_loop().root.is_node_ready():
+ await Engine.get_main_loop().root.ready
+ for __ : int in range(30):
+ var scene : SceneTree = get_tree()
+ if !is_instance_valid(scene):
+ return
+ await scene.process_frame
+
+func _input(event: InputEvent) -> void:
+ if event.is_pressed() and event.is_match(_c_input_switch_panels):
+ _on_pop_pressed(_id_switch_panels)
+
+func toggle_position() -> void:
+ var container : Control = _container
+ if container:
+ if !_as_separate_container:
+ container = container.get_parent()
+
+ if null == container:
+ return
+
+ var parent : Control = container.get_parent()
+
+ if parent is HSplitContainer and parent.get_child_count() > 1:
+ if container.get_index() != 0:
+ var size : float = (parent.get_child(0) as Control).size.x
+ parent.move_child(container, 0)
+ parent.split_offset = -size
+ parent.clamp_split_offset.call_deferred()
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if is_instance_valid(settings):
+ settings.set_setting("plugin/godot_ide/fancy_filters_script/script_list_and_filter_to_right", false)
+ else:
+ var size : float = (parent.get_child(1) as Control).size.x
+ parent.move_child(container, parent.get_child_count() - 1)
+ parent.split_offset = size
+ parent.clamp_split_offset.call_deferred()
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if is_instance_valid(settings):
+ settings.set_setting("plugin/godot_ide/fancy_filters_script/script_list_and_filter_to_right", true)
+
+func _exit_tree() -> void:
+ var container : VSplitContainer = IDE.get_script_list_container()
+
+ if is_instance_valid(_container) and _container.is_inside_tree():
+ IDE.set_config("fancy_filters_script", "script_list_and_filter_to_right", _container.get_index() > 0)
+
+ if container:
+ var current_parent : Node = container.get_parent()
+
+ if _placeholder:
+ for x : Node in _placeholder.get_children():
+ x.reparent(container)
+ _placeholder.queue_free()
+ _placeholder = null
+
+ if current_parent != _parent:
+ if current_parent == null:
+ _parent.add_child(container)
+ else:
+ container.reparent(_parent)
+ for x : Node in container.get_children():
+ if x is Control:
+ x.visible = true
+ if is_instance_valid(_container):
+ _container.queue_free()
+ _container = null
+ container.visible = true
+
+ var parent : Control = container.get_parent()
+ if parent is HSplitContainer:
+ if container.get_index() != 0:
+ var size : float = (parent.get_child(1) as Control).size.x
+ parent.move_child(container, 0)
+ parent.split_offset = -size
+ parent.clamp_split_offset.call_deferred()
+
+ #TODO: Remove new menu buttons
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd.uid
new file mode 100644
index 0000000..69a7f23
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/plugin.gd.uid
@@ -0,0 +1 @@
+uid://bh2804ipay0ks
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd
new file mode 100644
index 0000000..594580c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd
@@ -0,0 +1,49 @@
+@tool
+extends Popup
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+
+@export var override_copy_button : Button
+
+var callback : Callable
+
+func _ready() -> void:
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return
+ get_child(0). add_theme_stylebox_override(&"panel", control.get_theme_stylebox(&"panel", &""))
+
+
+func enable_copy_override(e : bool) -> void:
+ override_copy_button.disabled = !e
+
+func override_copy() -> void:
+ if callback.is_valid():
+ callback.call(&"override_copy")
+ close()
+
+func copy() -> void:
+ if callback.is_valid():
+ callback.call(&"copy")
+ close()
+
+func close() -> void:
+ hide()
+ queue_free()
+
+func goto() -> void:
+ if callback.is_valid():
+ callback.call(&"goto")
+
+
+func _on_popup_hide() -> void:
+ close()
+
+
+func _on_close_requested() -> void:
+ close()
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd.uid
new file mode 100644
index 0000000..4602b5b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd.uid
@@ -0,0 +1 @@
+uid://cmcd05m1t5hwr
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.tscn b/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.tscn
new file mode 100644
index 0000000..42d4266
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.tscn
@@ -0,0 +1,76 @@
+[gd_scene load_steps=4 format=3 uid="uid://d1v4xxafti6nw"]
+
+[ext_resource type="Script" uid="uid://cmcd05m1t5hwr" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.gd" id="1_x40uf"]
+[ext_resource type="Script" uid="uid://b1v225muollg5" path="res://addons/_Godot-IDE_/plugins/fancy_filters_script/button.gd" id="2_l7727"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l7727"]
+content_margin_left = 5.0
+content_margin_top = 5.0
+content_margin_right = 5.0
+content_margin_bottom = 5.0
+bg_color = Color(0.115499996, 0.132, 0.15949999, 1)
+corner_detail = 1
+anti_aliasing = false
+
+[node name="PopTree" type="Popup" node_paths=PackedStringArray("override_copy_button")]
+oversampling_override = 1.0
+title = "Member Selected"
+position = Vector2i(496, 231)
+size = Vector2i(161, 189)
+visible = true
+content_scale_aspect = 4
+script = ExtResource("1_x40uf")
+override_copy_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/override_copy")
+
+[node name="PanelContainer" type="PanelContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_l7727")
+
+[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+text = "Select Option"
+horizontal_alignment = 1
+
+[node name="HSeparator" type="HSeparator" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+
+[node name="copy" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+text = "Copy"
+script = ExtResource("2_l7727")
+
+[node name="override_copy" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+text = "Copy for override"
+script = ExtResource("2_l7727")
+
+[node name="goto" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+text = "Go to member"
+script = ExtResource("2_l7727")
+
+[node name="close" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 10
+text = "Close"
+script = ExtResource("2_l7727")
+
+[connection signal="close_requested" from="." to="." method="_on_close_requested"]
+[connection signal="popup_hide" from="." to="." method="_on_popup_hide"]
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd b/addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd
new file mode 100644
index 0000000..1ff9e93
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd
@@ -0,0 +1,1149 @@
+@tool
+extends Control
+# =============================================================================
+# Author: Twister
+# Fancy Filter Script
+#
+# Addon for Godot
+# =============================================================================
+
+const PUBLIC_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/func_public.svg")
+const PRIVATE_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/func_private.svg")
+const PROTECTED_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/func_virtual.svg")
+const STATIC_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/static.svg")
+const CONST_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/static.svg")
+const EXPORT_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg")
+const OVERRIDED_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MethodOverride.svg")
+const CHECKED_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/check.svg")
+
+const SCRIPT_TOOL_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/Tools.svg")
+const SCRIPT_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/Script.svg")
+const SCRIPT_EXTEND_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/ScriptExtend.svg")
+const SCRIPT_ABSTRACT_ICON : Texture2D = SCRIPT_ICON
+const SCRIPT_NATIVE_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/InterfaceScript.svg")
+
+const MEMBER_ANNOTATION_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg")
+const MEMBER_CONSTANT_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberConstant.svg")
+#const MEMBER_CONSTRUCTOR_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberConstructor.svg")
+const MEMBER_METHOD_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberMethod.svg")
+#const MEMBER_OPERATOR_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberOperator.svg")
+const MEMBER_PROPERTY_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberProperty.svg")
+const MEMBER_SIGNAL_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MemberSignal.svg")
+const MEMBER_OVERRIDE_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/MethodOverride.svg")
+
+enum SORT_NAME_TYPE{
+ NONE = 0,
+ ORDER_NAME_NORMAL = 1,
+ ORDER_NAME_INVERT = 2#,
+ #ORDER_ACCESSIBILITY_NORMAL,
+ #ORDER_ACCESSIBILITY_INVERT
+}
+
+@export var button_container : Control = null
+@export var tree_container : Tree = null
+
+var DOTS_ICON : Texture2D = null
+
+#region CONFIG
+var use_colors_in_tittles : bool = false
+var use_dots_as_item_icons : bool = false
+var use_background_color_in_script_info : bool = false
+
+var show_properties : bool = true
+var show_signals : bool = true
+var show_constants : bool = true
+var show_parent_class : bool = true
+var show_native_class : bool = false
+var show_functions : bool = true
+var show_inheritance : bool = true
+
+var flat_mode : bool = false
+
+var show_properties_color : Color = Color("bce0ff")
+var show_signals_color : Color = Color("e5d1ff")
+var show_constants_color : Color = Color("00ffff")
+var show_parent_class_color : Color = Color("76cfb5")
+var show_native_class_color : Color = Color("76cfb5")
+var show_function_color : Color = Color("66e6ff")
+
+var properties_color_item : Color = Color("bce0ff")
+var signals_color_item : Color = Color("e5d1ff")
+var constants_color_item : Color = Color("64ffff")
+#var parent_class_color_item : Color = show_native_class_color
+#var native_class_color_item : Color = show_native_class_color
+var function_color_item : Color = Color("66e6ff")
+var inheritance_color_item : Color = Color("76cfb5")
+
+
+#var accessibility_order_by : Array[int] = [0,1,2,3,4,5]
+var members_order_by : Array[int] = [0,1,2,3]
+var name_order_by : SORT_NAME_TYPE = SORT_NAME_TYPE.NONE
+#endregion
+
+var _pop : Popup = null
+
+var _buffer : Dictionary = {}
+var _last : Variant = null
+
+#var accessibility : AccesibilityOrder = null
+
+#func _init() -> void:
+ #accessibility = AccesibilityOrder.new(accessibility_order_by.size())
+
+func _is_in_change(plugin : String, item : String, array : PackedStringArray) -> bool:
+ item = plugin.path_join(item)
+ for x : String in array:
+ if x.ends_with(item):
+ return true
+ return false
+
+func _setup(changes : PackedStringArray = []) -> void:
+ var PLUGIN : String = "fancy_filters_script"
+ var dirty : bool = false
+
+ for x : String in [
+ "show_properties"
+ ,"show_signals"
+ ,"show_constants"
+ ,"show_parent_class"
+ ,"show_native_class"
+ ,"show_functions"
+ ,"show_inheritance"
+ ,"use_colors_in_tittles"
+ ,"use_dots_as_item_icons"
+ , "use_background_color_in_script_info"
+ ,"show_properties_color"
+ ,"show_signals_color"
+ ,"show_constants_color"
+ #,"show_parent_class_color"
+ #,"show_native_class_color"
+ ,"show_function_color"
+ ,"properties_color_item"
+ ,"signals_color_item"
+ ,"constants_color_item"
+ #,"parent_class_color_item"
+ #,"native_class_color_item"
+ ,"function_color_item"
+ ,"inheritance_color_item"
+ #,"accessibility_order_by"
+ ,"members_order_by"
+ ,"name_order_by"
+ ,"flat_mode"
+ ]:
+ if changes.size() == 0 or _is_in_change(PLUGIN, x, changes):
+ var value : Variant = get(x)
+ if value != null:
+ var current : Variant = IDE.get_config(PLUGIN, x)
+ if typeof(current) == typeof(value):
+ set(x, current)
+ dirty = true
+ else:
+ IDE.set_config(PLUGIN, x, value)
+ else:
+ push_warning("Its broke! > ", x)
+
+ if use_dots_as_item_icons:
+ if DOTS_ICON == null:
+ DOTS_ICON = ResourceLoader.load("res://addons/_Godot-IDE_/shared_resources/dot.svg")
+ else:
+ DOTS_ICON = null
+
+ if changes.size() > 0 and dirty:
+ propagate_call(&"update_settings")
+ force_update()
+
+func _on_settings_changed() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings:
+ var changes : PackedStringArray = settings.get_changed_settings()
+ _setup(changes)
+
+func _enter_tree() -> void:
+ _setup()
+
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ if !editor.editor_script_changed.is_connected(_on_change_script):
+ editor.editor_script_changed.connect(_on_change_script)
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings:
+ if !settings.settings_changed.is_connected(_on_settings_changed):
+ settings.settings_changed.connect(_on_settings_changed)
+
+
+func _exit_tree() -> void:
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ if editor.editor_script_changed.is_connected(_on_change_script):
+ editor.editor_script_changed.disconnect(_on_change_script)
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings:
+ if settings.settings_changed.is_connected(_on_settings_changed):
+ settings.settings_changed.disconnect(_on_settings_changed)
+
+func enable_filter(filter_name : StringName, value : bool) -> void:
+ if filter_name == &"show_all":
+ var buttons : Array[Node] = button_container.get_children()
+ if buttons[0] is Button:
+ var val : bool = buttons[0].button_pressed
+ for node : Node in buttons:
+ if node is Button:
+ node.button_pressed = val
+ if get(node.name) != null:
+ set(node.name, value)
+ if node.has_method(&"visual"):
+ node.call(&"visual")
+ else:
+ if get(filter_name) != null:
+ set(filter_name, value)
+ var buttons : Array[Node] = button_container.get_children()
+ var all : Button = buttons[0]
+ all.button_pressed = true
+ for node : Node in buttons:
+ if node is Button:
+ if node.button_pressed == false and node != all:
+ all.button_pressed = false
+ break
+ if all.has_method(&"visual"):
+ all.call(&"visual")
+ force_update()
+
+func _on_collapsed(item : TreeItem) -> void:
+ var meta : Variant = item.get_metadata(0)
+ if meta is String:
+ _buffer[meta] = item.collapsed
+
+func _process(_delta: float) -> void:
+ set_process(false)
+
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ var sc : Script = _last
+ _last = null
+ if editor:
+ var nsc : Script = editor.get_current_script()
+ if nsc:
+ sc = nsc
+ _on_change_script(sc)
+
+func force_update() -> void:
+ set_process(true)
+
+func _on_activate() -> void:
+ if tree_container:
+ if is_instance_valid(_last):
+ var current : Script = _last
+ var item : TreeItem = tree_container.get_selected()
+ if !item:
+ return
+ var symbol_name : String = item.get_text(1).strip_edges().split(" ", false, 1)[0]
+ while true:
+ var st : String = current.resource_path
+ if !FileAccess.file_exists(st):
+ return
+
+ var script_content : String = FileAccess.get_file_as_string(st)
+ var lines : PackedStringArray = script_content.split("\n", true)
+ var line_number : int = -1
+
+ var pattern : RegEx = RegEx.create_from_string("[\\s]*var[\\n\\t\\s]+\\b" + symbol_name + "\\b.*|\\s*const[\\n\\t\\s]+\\b" + symbol_name + "\\b.*|\\s*func[\\n\\t\\s]+\\b" + symbol_name + "|\\s*signal[\\n\\t\\s]+\\b" + symbol_name)
+ if is_instance_valid(pattern) and pattern.is_valid():
+ for x : int in range(lines.size()):
+ var line : String = lines[x]
+ if pattern.search(line):
+ line_number = x
+ break
+
+ if line_number > -1:
+ var sce: ScriptEditor = EditorInterface.get_script_editor()
+ if !sce:
+ return
+ if sce.get_current_script() != current:
+ EditorInterface.edit_script(current, line_number, 0, true)
+ sce.goto_line(line_number)
+ return
+ var base : Script = current.get_base_script()
+ if base != null:
+ current = base
+ continue
+ break
+ var type : StringName = current.get_instance_base_type()
+ while !type.is_empty():
+ if ClassDB.class_exists(type):
+ var symbol : String = item.get_tooltip_text(0)
+ var prefx : String = ""
+ if symbol.is_empty():
+ symbol = item.get_tooltip_text(1)
+ if type == "GraphNode":
+ prefx = "class_theme_item"
+ if symbol.begins_with("@"):
+ prefx = "class_annotation"
+ elif ClassDB.class_has_signal(type, symbol_name):
+ prefx = "class_signal"
+ elif ClassDB.class_has_enum(type, symbol_name, true):
+ prefx = "class_constant"
+ elif ClassDB.class_has_integer_constant(type, symbol_name):
+ prefx = "class_constant"
+ else:
+ var list : Array[Dictionary] = ClassDB.class_get_property_list(type, true)
+ for x : Dictionary in list:
+ if x.name == symbol_name:
+ prefx = "class_property"
+ break
+ if prefx.is_empty():
+ list = ClassDB.class_get_method_list(type, true)
+ for x : Dictionary in list:
+ if x.name == symbol_name:
+ prefx = "class_method"
+ break
+ if !prefx.is_empty():
+ var path : String = "{0}:{1}:{2}".format([prefx, type, symbol_name])
+ EditorInterface.get_script_editor().goto_help(path)
+ return
+ type = ClassDB.get_parent_class(type)
+ continue
+ break
+
+func _copy(fnc : String,data : String, type : int) -> void:
+ if type == 1:
+ data = (data.trim_prefix("func ").split(")", false, 1)[0] + ")").replace(" : ", "_")
+ elif type == 2:
+ var packed : PackedStringArray = data.trim_prefix("func ").split("(", false, 1)
+ data = packed[0] + ".emit("
+ data += (packed[1].split(")", true, 1)[0] + ")").replace(" : ", "_")
+ elif type == 3:
+ data = fnc
+ else:
+ var packed : PackedStringArray = data.split("\n", false, 0)
+ if packed.size() > 1:
+ data = str(packed[0], " ", packed[packed.size() - 1].strip_edges())
+ DisplayServer.clipboard_set(data)
+ print("Copied in clipboard: [", fnc, "] use ctrl + v for paste!")
+
+func _on_pop_selection(option : StringName, item : TreeItem) -> void:
+ if is_instance_valid(item):
+ var parent : TreeItem = item.get_parent()
+ if parent:
+ if option == &"goto":
+ tree_container.item_activated.emit()
+ elif option == &"copy" or option == &"override_copy":
+ var icon : Texture2D = parent.get_icon(0)
+ if icon == null:
+ icon = item.get_icon(0)
+ if !is_instance_valid(icon):
+ return
+ var itype : int = 3
+ if icon == MEMBER_METHOD_ICON:
+ if option == &"override_copy":
+ itype = 0
+ else:
+ itype = 1
+ elif icon == MEMBER_SIGNAL_ICON:
+ itype = 2
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var sc : Script = editor.get_current_script()
+ if sc:
+ var func_ : StringName = item.get_text(1).strip_edges().get_slice(" ", 0)
+ if itype == 3:
+ _copy(func_, func_, itype)
+ return
+ if itype != 2:
+ var base : Script = sc
+ while base != null:
+ for d : Dictionary in base.get_script_method_list():
+ if d.name == func_:
+ _copy(func_, IDE.get_header_function(d),itype)
+ return
+ for d : Dictionary in base.get_method_list():
+ if d.name == func_:
+ _copy(func_, IDE.get_header_function(d),itype)
+ return
+ var nbase : Script = base.get_base_script()
+ if nbase != null:
+ base = nbase
+ break
+ if base != null:
+ var type : StringName = base.get_instance_base_type()
+ if ClassDB.class_exists(type):
+ for d : Dictionary in ClassDB.class_get_method_list(type, false):
+ if d.name == func_:
+ _copy(func_, IDE.get_header_function(d),itype)
+ return
+ else:
+ var base : Script = sc
+ while base != null:
+ for d : Dictionary in base.get_script_signal_list():
+ if d.name == func_:
+ _copy(func_, IDE.get_header_function(d),itype)
+ return
+ for d : Dictionary in base.get_signal_list():
+ if d.name == func_:
+ _copy(func_, IDE.get_header_function(d),itype)
+ return
+ var nbase : Script = base.get_base_script()
+ if nbase != null:
+ base = nbase
+ break
+ if base != null:
+ var type : StringName = base.get_instance_base_type()
+ if ClassDB.class_exists(type):
+ for d : Dictionary in ClassDB.class_get_signal_list(type, false):
+ if d.name == func_:
+ _copy(func_, IDE.get_header_function(d),itype)
+ return
+
+
+func _on_mouse(_mouse_position: Vector2, mouse_button_index: int) -> void:
+ if mouse_button_index == MOUSE_BUTTON_RIGHT:
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var sc : Script = editor.get_current_script()
+ if !is_instance_valid(sc):
+ print("[INFO] The current editor does not have script focus!")
+ return
+
+ var item : TreeItem = tree_container.get_selected()
+ if item:
+ var parent : TreeItem = item.get_parent()
+ if parent:
+ var icon : Texture2D = parent.get_icon(0)
+ if icon == null:
+ icon = item.get_icon(0)
+ if !is_instance_valid(icon):
+ return
+ for x : Texture2D in [
+ MEMBER_CONSTANT_ICON,
+ MEMBER_METHOD_ICON,
+ MEMBER_SIGNAL_ICON,
+ MEMBER_PROPERTY_ICON
+ ]:
+ if icon == x:
+ var is_first : bool = true
+ parent = parent.get_parent()
+ if parent and parent.get_index() > 0:
+ is_first = false
+ if !is_instance_valid(_pop):
+ var res : PackedScene = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/fancy_filters_script/pop_tree.tscn")
+ _pop = res.instantiate()
+ add_child(_pop)
+ _pop.callback = _on_pop_selection.bind(item)
+ _pop.enable_copy_override(MEMBER_METHOD_ICON == icon and (!is_first or flat_mode))
+
+ var os_name : String = OS.get_name()
+ match os_name:
+ "macOS":
+ var mouse_global : Vector2 = get_global_mouse_position()
+ var offset : Vector2 = Vector2(10.0, 10.0) #HINT: dot scale: interface/editor/display_scale ## I leave this here, maybe use for a future testing and update.
+ var popup_rect : Rect2 = Rect2(mouse_global + offset, Vector2.ZERO)
+ _pop.popup_on_parent(popup_rect)
+ "iOS":
+ _pop.popup_centered()
+ _:
+ # Others platforms.
+ _pop.position = get_global_mouse_position()
+ _pop.popup()
+ return
+
+
+func _custom_order(s1 : String, s2 : String) -> bool:
+ for x : int in mini(s1.length(), s2.length()):
+ var c1 : String = s1[x].to_lower()
+ var c2 : String = s2[x].to_lower()
+ if c1 == c2:
+ continue
+ return c1 < c2
+ return false
+
+func _order_name(keys : Array) -> Array:
+ if name_order_by != SORT_NAME_TYPE.NONE:
+ if name_order_by == SORT_NAME_TYPE.ORDER_NAME_NORMAL:
+ keys.sort_custom(_custom_order)
+ elif name_order_by == SORT_NAME_TYPE.ORDER_NAME_INVERT:
+ keys.sort_custom(_custom_order)
+ keys.reverse()
+ return keys
+
+func _on_change_script(script : Script) -> void:
+ if _last == script:
+ return
+ _last = script
+ tree_container.clear()
+ if script == null:
+ return
+ var data : Dictionary = IDE.get_script_properties_list(script)
+ var root : TreeItem = tree_container.create_item()
+ tree_container.columns = 1#3
+ var path : String = script.resource_path
+ if !path.is_empty():
+ root.set_text(0, "[{0}]".format([path.get_file()]))
+ root.set_tooltip_text(0, path)
+ else:
+ root.set_text(0, "Info")
+
+ if _buffer.size() > 40:
+ _buffer.clear()
+
+ if !tree_container.item_collapsed.is_connected(_on_collapsed):
+ tree_container.item_collapsed.connect(_on_collapsed)
+
+ if !tree_container.item_activated.is_connected(_on_activate):
+ tree_container.item_activated.connect(_on_activate)
+
+ if !tree_container.item_mouse_selected.is_connected(_on_mouse):
+ tree_container.allow_rmb_select = true
+ tree_container.item_mouse_selected.connect(_on_mouse)
+
+
+ var private_methods : String = IDE.PRIVATE_METHODS
+ var protected_methods : String = IDE.VIRTUAL_METHODS
+ tree_container.columns = 2
+ tree_container.set_column_expand(0, true)
+ tree_container.set_column_expand(1, true)
+ tree_container.set_column_custom_minimum_width(0, 0)
+ tree_container.set_column_clip_content(0, false)
+ tree_container.set_column_expand_ratio(0, 0)
+ tree_container.set_column_custom_minimum_width(1, 200)
+ root.set_expand_right(0, true)
+ root.set_selectable(0, false)
+ root.set_expand_right(1, true)
+ root.set_selectable(1, false)
+ root.disable_folding = true
+
+ var BASE_COLOR : Color = root.get_custom_color(0)
+ if BASE_COLOR == Color.BLACK:
+ BASE_COLOR = Color.WHITE
+
+ var PRIMARY_COLOR : Color = BASE_COLOR.darkened(0.2)
+ var SECONDARY_COLOR : Color = BASE_COLOR.darkened(0.4)
+
+ var public_icon : Texture2D = PUBLIC_ICON
+ var private_icon : Texture2D = PRIVATE_ICON
+ var virtual_icon : Texture2D = PROTECTED_ICON
+ var public_icon_modulate: Color = Color.WHITE
+ var private_icon_modulate: Color = Color.WHITE
+ var virtual_icon_modulate: Color = Color.WHITE
+
+ if use_dots_as_item_icons and null != DOTS_ICON:
+ public_icon = DOTS_ICON
+ private_icon = DOTS_ICON
+ virtual_icon = DOTS_ICON
+ public_icon_modulate = Color.GREEN
+ private_icon_modulate = Color.YELLOW
+ virtual_icon_modulate = Color.REBECCA_PURPLE
+
+ var index : int = -1
+ var src : String = script.source_code
+ var track_override : Dictionary[StringName, bool] = {}
+
+ if flat_mode:
+ var values : Dictionary = {}
+ for sc : Dictionary in data.values():
+ index += 1
+ if index > 0:
+ var native : bool = sc["path"] == "NativeScript"
+ if native and !show_native_class:
+ continue
+ elif !native and !show_parent_class:
+ continue
+
+
+ var sc_data : Dictionary = {}
+ for order : int in members_order_by:
+ if order == 0 and show_properties:
+ if !values.has(order):
+ values[order] = []
+ sc_data = sc["properties"]
+ if sc_data.size() > 0:
+ values[order].append(sc_data)
+
+
+ elif order == 1 and show_functions:
+ sc_data = sc["functions"]
+ if sc_data.size() > 0:
+ if !values.has(order):
+ values[order] = []
+ values[order].append(sc_data)
+
+ elif order == 2 and show_signals:
+ sc_data = sc["signals"]
+ if sc_data.size() > 0:
+ if !values.has(order):
+ values[order] = []
+ values[order].append(sc_data)
+
+
+ elif order == 3 and show_constants:
+ sc_data = sc["constants"]
+ if sc_data.size() > 0:
+ if !values.has(order):
+ values[order] = []
+ values[order].append(sc_data)
+
+
+ for order : int in values.keys():
+ var tree_item : TreeItem = null
+ var meta : String = ""
+ tree_item = root
+ for sc_data : Dictionary in values[order]:
+ if order == 0:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if properties_color_item != Color.WHITE:
+ item_color = properties_color_item
+
+ mthds = tree_item
+ var TRANSPARENT : Color = show_properties_color
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if !show_inheritance:
+ continue
+ override = true
+ var _item : TreeItem = mthds.create_child()
+ var text : String = " {0} : {1}".format([packed[0], packed[1]])
+ _item.set_text(1, text)
+ _item.set_icon(0, MEMBER_PROPERTY_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if "export" in packed:
+ _item.set_icon(1, EXPORT_ICON)
+ _item.set_tooltip_text(1, str("@export var ", text))
+ #accessibility.add(0, fnc)
+ elif "static" in packed:
+ _item.set_icon(1, STATIC_ICON)
+ _item.set_tooltip_text(1, str("static var ", text))
+ #accessibility.add(1, fnc)
+ elif "const" in packed:
+ _item.set_icon(1, CONST_ICON)
+ _item.set_tooltip_text(1, str("const ", text))
+ #accessibility.add(2, fnc)
+ elif fnc.begins_with(private_methods):
+ _item.set_icon(1, private_icon)
+ _item.set_icon_modulate(1, private_icon_modulate)
+ #accessibility.add(3, fnc)
+ elif fnc.begins_with(protected_methods):
+ _item.set_icon(1, virtual_icon)
+ _item.set_icon_modulate(1, virtual_icon_modulate)
+ #accessibility.add(4, fnc)
+ else:
+ _item.set_icon(1, public_icon)
+ _item.set_icon_modulate(1, public_icon_modulate)
+ #accessibility.add(5, fnc)
+ if override:
+ #accessibility.add_overrided(fnc)
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if use_background_color_in_script_info:
+ var c : Color = show_properties_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+ elif order == 1:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if function_color_item != Color.WHITE:
+ item_color = function_color_item
+
+ mthds = tree_item
+
+ var TRANSPARENT : Color = show_function_color
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if index > 0:
+ if !show_inheritance:
+ continue
+ else:
+ if !show_inheritance:
+ var rgx : RegEx = RegEx.create_from_string("func[\\s\\t\\n]*\\b{0}[\\s\\t\\n]*\\(".format([fnc]))
+ if is_instance_valid(rgx) and rgx.is_valid():
+ if null == rgx.search(src):
+ continue
+ track_override[fnc] = true
+ override = show_inheritance
+
+ var _item : TreeItem = mthds.create_child()
+ var text : String = " {0} ( {1} ) -> {2}".format([packed[0], packed[1], packed[2]])
+
+ _item.set_icon(0, MEMBER_METHOD_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if "static" in packed:
+ _item.set_icon(1, STATIC_ICON)
+ _item.set_tooltip_text(1, str("static var ", text))
+ elif "const" in packed:
+ _item.set_icon(1, CONST_ICON)
+ _item.set_tooltip_text(1, str("const ", text))
+ elif fnc.begins_with(private_methods):
+ _item.set_icon(1, private_icon)
+ _item.set_icon_modulate(1, private_icon_modulate)
+ elif fnc.begins_with(protected_methods):
+ _item.set_icon(1, virtual_icon)
+ _item.set_icon_modulate(1, virtual_icon_modulate)
+ else:
+ _item.set_icon(1, public_icon)
+ _item.set_icon_modulate(1, public_icon_modulate)
+ if override:
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if index > 0:
+ if track_override.has(fnc):
+ _item.set_icon_overlay(1, CHECKED_ICON)
+ _item.set_text(1, text)
+ if use_background_color_in_script_info:
+ var c : Color = show_function_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+ elif order == 2:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if signals_color_item != Color.WHITE:
+ item_color = signals_color_item
+
+ mthds = tree_item
+
+
+ var TRANSPARENT : Color = show_signals_color
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if !show_inheritance:
+ continue
+ override = true
+ var _item : TreeItem = mthds.create_child()
+ _item.set_text(1, " {0} ( {1} ) -> {2}".format([packed[0], packed[1], packed[2]]))
+
+ _item.set_icon(0, MEMBER_SIGNAL_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if override:
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if use_background_color_in_script_info:
+ var c : Color = show_signals_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+ elif order == 3:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if constants_color_item != Color.WHITE:
+ item_color = constants_color_item
+
+ mthds = tree_item
+
+ var TRANSPARENT : Color = show_constants_color
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if !show_inheritance:
+ continue
+ override = true
+ var _item : TreeItem = mthds.create_child()
+ _item.set_text(1, " {0} : {1}".format([packed[0], packed[1]]))
+
+ _item.set_icon(0, MEMBER_CONSTANT_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if override:
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if use_background_color_in_script_info:
+ var c : Color = show_constants_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+ else:
+ for sc : Dictionary in data.values():
+ index += 1
+ if index > 0:
+ var native : bool = sc["path"] == "NativeScript"
+ if native and !show_native_class:
+ continue
+ elif !native and !show_parent_class:
+ continue
+
+ var tree_item : TreeItem = null
+ var meta : String = ""
+
+
+ tree_item = root.create_child()
+ meta = str("C", index)
+ tree_item.set_text(0, str(" ",sc["name"]))
+ tree_item.set_tooltip_text(1, sc["path"])
+ tree_item.set_metadata(0, meta)
+ tree_item.set_custom_color(0, BASE_COLOR)
+ tree_item.set_icon_modulate(0, Color.WHITE)
+ tree_item.set_expand_right(0, true)
+ #tree_item.set_text_alignment(0, HORIZONTAL_ALIGNMENT_CENTER)
+ if _buffer.has(meta):
+ tree_item.collapsed = _buffer[meta]
+ if sc["tool"]:
+ tree_item.set_icon(0, SCRIPT_TOOL_ICON)
+ tree_item.set_icon_modulate(0, Color.DEEP_SKY_BLUE)
+ if index > 0:
+ tree_item.set_icon_overlay(0, OVERRIDED_ICON)
+ elif sc["abstract"]:
+ tree_item.set_icon(0, SCRIPT_ABSTRACT_ICON)
+ if index > 0:
+ tree_item.set_icon_overlay(0, OVERRIDED_ICON)
+ elif sc["path"] == "NativeScript":
+ tree_item.set_icon(0, SCRIPT_NATIVE_ICON)
+ else:
+ if index > 0:
+ tree_item.set_icon(0, SCRIPT_EXTEND_ICON)
+ else:
+ tree_item.set_icon(0, SCRIPT_ICON)
+ tree_item.set_selectable(0, false)
+ tree_item.set_selectable(1, false)
+
+ var sc_data : Dictionary = {}
+ for order : int in members_order_by:
+ if order == 0 and show_properties:
+ sc_data = sc["properties"]
+ if sc_data.size() > 0:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if properties_color_item != Color.WHITE:
+ item_color = properties_color_item
+
+ mthds = tree_item.create_child()
+
+ mthds.set_text(0, " Properties")
+ mthds.set_selectable(0, false)
+ mthds.set_selectable(1, false)
+ mthds.set_expand_right(0, true)
+ mthds.set_icon(0, MEMBER_PROPERTY_ICON)
+
+ if use_background_color_in_script_info:
+ var c : Color = show_properties_color
+ c.a = 0.15
+ mthds.set_custom_bg_color(0, c)
+ mthds.set_custom_bg_color(1, c)
+ if use_colors_in_tittles:
+ mthds.set_custom_color(0, show_properties_color)
+ else:
+ mthds.set_custom_color(0, PRIMARY_COLOR)
+ meta = str("P", index)
+ mthds.set_metadata(0, meta)
+ mthds.set_icon_modulate(0, show_properties_color)
+ if _buffer.has(meta):
+ mthds.collapsed = _buffer[meta]
+ else:
+ mthds.collapsed = true
+
+ var TRANSPARENT : Color = show_properties_color
+ TRANSPARENT.a = 0.4
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if !show_inheritance:
+ continue
+ override = true
+ var _item : TreeItem = mthds.create_child()
+ var text : String = " {0} : {1}".format([packed[0], packed[1]])
+ _item.set_text(1, text)
+ _item.set_icon(0, MEMBER_PROPERTY_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if "export" in packed:
+ _item.set_icon(1, EXPORT_ICON)
+ _item.set_tooltip_text(1, str("@export var ", text))
+ #accessibility.add(0, fnc)
+ elif "static" in packed:
+ _item.set_icon(1, STATIC_ICON)
+ _item.set_tooltip_text(1, str("static var ", text))
+ #accessibility.add(1, fnc)
+ elif "const" in packed:
+ _item.set_icon(1, CONST_ICON)
+ _item.set_tooltip_text(1, str("const ", text))
+ #accessibility.add(2, fnc)
+ elif fnc.begins_with(private_methods):
+ _item.set_icon(1, private_icon)
+ _item.set_icon_modulate(1, private_icon_modulate)
+ #accessibility.add(3, fnc)
+ elif fnc.begins_with(protected_methods):
+ _item.set_icon(1, virtual_icon)
+ _item.set_icon_modulate(1, virtual_icon_modulate)
+ #accessibility.add(4, fnc)
+ else:
+ _item.set_icon(1, public_icon)
+ _item.set_icon_modulate(1, public_icon_modulate)
+ #accessibility.add(5, fnc)
+ if override:
+ #accessibility.add_overrided(fnc)
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if use_background_color_in_script_info:
+ var c : Color = show_properties_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+ elif order == 1 and show_functions:
+ sc_data = sc["functions"]
+ if sc_data.size() > 0:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if function_color_item != Color.WHITE:
+ item_color = function_color_item
+
+ mthds = tree_item.create_child()
+
+ mthds.set_text(0, " Methods")
+ mthds.set_selectable(0, false)
+ mthds.set_selectable(1, false)
+ mthds.set_expand_right(0, true)
+ mthds.set_icon(0, MEMBER_METHOD_ICON)
+ if use_background_color_in_script_info:
+ var c : Color = show_function_color
+ c.a = 0.15
+ mthds.set_custom_bg_color(0, c)
+ mthds.set_custom_bg_color(1, c)
+ if use_colors_in_tittles:
+ mthds.set_custom_color(0, show_function_color)
+ else:
+ mthds.set_custom_color(0, PRIMARY_COLOR)
+ meta = str("F", index)
+ mthds.set_metadata(0, meta)
+ mthds.set_icon_modulate(0, show_function_color)
+ if _buffer.has(meta):
+ mthds.collapsed = _buffer[meta]
+ else:
+ mthds.collapsed = true
+
+ var TRANSPARENT : Color = show_function_color
+ TRANSPARENT.a = 0.4
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if index > 0:
+ if !show_inheritance:
+ continue
+ else:
+ if !show_inheritance:
+ var rgx : RegEx = RegEx.create_from_string("func[\\s\\t\\n]*\\b{0}[\\s\\t\\n]*\\(".format([fnc]))
+ if is_instance_valid(rgx) and rgx.is_valid():
+ if null == rgx.search(src):
+ continue
+ track_override[fnc] = true
+ override = show_inheritance
+
+ var _item : TreeItem = mthds.create_child()
+ var text : String = " {0} ( {1} ) -> {2}".format([packed[0], packed[1], packed[2]])
+
+ _item.set_icon(0, MEMBER_METHOD_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if "static" in packed:
+ _item.set_icon(1, STATIC_ICON)
+ _item.set_tooltip_text(1, str("static var ", text))
+ elif "const" in packed:
+ _item.set_icon(1, CONST_ICON)
+ _item.set_tooltip_text(1, str("const ", text))
+ elif fnc.begins_with(private_methods):
+ _item.set_icon(1, private_icon)
+ _item.set_icon_modulate(1, private_icon_modulate)
+ elif fnc.begins_with(protected_methods):
+ _item.set_icon(1, virtual_icon)
+ _item.set_icon_modulate(1, virtual_icon_modulate)
+ else:
+ _item.set_icon(1, public_icon)
+ _item.set_icon_modulate(1, public_icon_modulate)
+ if override:
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if index > 0:
+ if track_override.has(fnc):
+ _item.set_icon_overlay(1, CHECKED_ICON)
+ _item.set_text(1, text)
+ if use_background_color_in_script_info:
+ var c : Color = show_function_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+ elif order == 2 and show_signals:
+ sc_data = sc["signals"]
+ if sc_data.size() > 0:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if signals_color_item != Color.WHITE:
+ item_color = signals_color_item
+
+ mthds = tree_item.create_child()
+
+ mthds.set_text(0, " Signals")
+ mthds.set_selectable(0, false)
+ mthds.set_selectable(1, false)
+ mthds.set_expand_right(0, true)
+ mthds.set_icon(0, MEMBER_SIGNAL_ICON)
+ if use_background_color_in_script_info:
+ var c : Color = show_signals_color
+ c.a = 0.15
+ mthds.set_custom_bg_color(0, c)
+ mthds.set_custom_bg_color(1, c)
+ if use_colors_in_tittles:
+ mthds.set_custom_color(0, show_signals_color)
+ else:
+ mthds.set_custom_color(0, PRIMARY_COLOR)
+ meta = str("S", index)
+ mthds.set_metadata(0, meta)
+ mthds.set_icon_modulate(0, show_signals_color)
+ if _buffer.has(meta):
+ mthds.collapsed = _buffer[meta]
+ else:
+ mthds.collapsed = true
+
+ var TRANSPARENT : Color = show_signals_color
+ TRANSPARENT.a = 0.4
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if !show_inheritance:
+ continue
+ override = true
+ var _item : TreeItem = mthds.create_child()
+ _item.set_text(1, " {0} ( {1} ) -> {2}".format([packed[0], packed[1], packed[2]]))
+
+ _item.set_icon(0, MEMBER_SIGNAL_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if override:
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if use_background_color_in_script_info:
+ var c : Color = show_signals_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+ elif order == 3 and show_constants:
+ sc_data = sc["constants"]
+ if sc_data.size() > 0:
+ var mthds : TreeItem = null
+ var item_color : Color = SECONDARY_COLOR
+ var override_item_color : Color = inheritance_color_item
+
+ if constants_color_item != Color.WHITE:
+ item_color = constants_color_item
+
+
+ mthds = tree_item.create_child()
+
+ mthds.set_text(0, " Constant")
+ mthds.set_selectable(0, false)
+ mthds.set_selectable(1, false)
+ mthds.set_expand_right(0, true)
+ mthds.set_icon(0, MEMBER_CONSTANT_ICON)
+ if use_background_color_in_script_info:
+ var c : Color = show_constants_color
+ c.a = 0.15
+ mthds.set_custom_bg_color(0, c)
+ mthds.set_custom_bg_color(1, c)
+ if use_colors_in_tittles:
+ mthds.set_custom_color(0, show_constants_color)
+ else:
+ mthds.set_custom_color(0, PRIMARY_COLOR)
+ meta = str("I", index)
+ mthds.set_metadata(0, meta)
+ mthds.set_icon_modulate(0, show_constants_color)
+ if _buffer.has(meta):
+ mthds.collapsed = _buffer[meta]
+ else:
+
+ mthds.collapsed = true
+
+ var TRANSPARENT : Color = show_constants_color
+ TRANSPARENT.a = 0.4
+
+ for fnc : StringName in _order_name(sc_data.keys()):
+ var packed : PackedStringArray = sc_data[fnc].split("||")
+ var override : bool = false
+ if "overrided" in packed:
+ if !show_inheritance:
+ continue
+ override = true
+ var _item : TreeItem = mthds.create_child()
+ _item.set_text(1, " {0} : {1}".format([packed[0], packed[1]]))
+
+ _item.set_icon(0, MEMBER_CONSTANT_ICON)
+ _item.set_icon_modulate(0, TRANSPARENT)
+ _item.set_selectable(0, false)
+
+ if override:
+ _item.set_icon_overlay(1, OVERRIDED_ICON)
+ _item.set_custom_color(1, override_item_color)
+ else:
+ _item.set_custom_color(1, item_color)
+ if use_background_color_in_script_info:
+ var c : Color = show_constants_color
+ c.a = 0.05
+ _item.set_custom_bg_color(0, c)
+ _item.set_custom_bg_color(1, c)
+
+
+func _ready() -> void:
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var sc : Script = editor.get_current_script()
+ if sc:
+ set_process(false)
+ _on_change_script(sc)
+ return
+ set_process(true)
diff --git a/addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd.uid b/addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd.uid
new file mode 100644
index 0000000..2a5fb12
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_filters_script/script_info.gd.uid
@@ -0,0 +1 @@
+uid://bwtq1pkujrb1d
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd
new file mode 100644
index 0000000..62a2c96
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd
@@ -0,0 +1,13 @@
+@tool
+extends Button
+# =============================================================================
+# Author: Twister
+# Fancy Search Class
+#
+# Addon for Godot
+# =============================================================================
+
+
+func _pressed() -> void:
+ if owner.has_method(name):
+ owner.call(name)
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd.uid
new file mode 100644
index 0000000..60fd8f1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd.uid
@@ -0,0 +1 @@
+uid://c6pfvkgkabvt6
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd
new file mode 100644
index 0000000..eaff23b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd
@@ -0,0 +1,66 @@
+@tool
+extends LineEdit
+# =============================================================================
+# Author: Twister
+# Fancy Search Class
+#
+# Addon for Godot
+# =============================================================================
+
+
+@export var tree : Tree = null
+
+func _ready() -> void:
+ text_changed.connect(sxrch)
+
+func reset() -> void:
+ if tree:
+ var root : TreeItem = tree.get_root()
+ if !root:
+ return
+ _reset(root)
+
+func _reset(root : TreeItem) -> void:
+ root.visible = true
+ for c : TreeItem in root.get_children():
+ _reset(c)
+
+func sxrch(txt : String) -> void:
+ reset()
+ if txt.is_empty():
+ return
+ if tree:
+ var root : TreeItem = tree.get_root()
+ if !root:
+ return
+ var rgx0 : RegEx = RegEx.create_from_string("(?i)\\b{0}\\b".format([txt]))
+ var rgx1 : RegEx = RegEx.create_from_string("(?i).*{0}.*".format([txt]))
+ var d0 : Array[TreeItem] = []
+ var d1 : Array[TreeItem] = []
+
+ for x : RegEx in [rgx0, rgx1]:
+ if !is_instance_valid(x) or !x.is_valid():
+ return
+
+ _sxrch(root, rgx0, rgx1, d0, d1)
+ root.visible = true
+
+ for t : TreeItem in d0:
+ _visible(t)
+ for t : TreeItem in d1:
+ _visible(t)
+
+func _visible(root : TreeItem) -> void:
+ if root:
+ root.visible = true
+ _visible(root.get_parent())
+
+func _sxrch(root : TreeItem, rgx0 : RegEx, rgx1 : RegEx, d0 : Array[TreeItem], d1 : Array[TreeItem]) -> void:
+ var txt : String = root.get_text(0)
+ root.visible = false
+ if rgx0.search(txt) != null:
+ d0.append(root)
+ elif d0.size() == 0 and rgx1.search(txt) != null:
+ d1.append(root)
+ for x : TreeItem in root.get_children():
+ _sxrch(x, rgx0, rgx1, d0, d1)
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd.uid
new file mode 100644
index 0000000..88d8b5e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd.uid
@@ -0,0 +1 @@
+uid://blmveofp20cm1
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd
new file mode 100644
index 0000000..f00ac91
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd
@@ -0,0 +1,383 @@
+@tool
+extends Window
+# =============================================================================
+# Author: Twister
+# Fancy Search Class
+#
+# Addon for Godot
+# =============================================================================
+
+
+@export var _container : TabContainer = null
+@export var _tree : Tree = null
+@export var _tree_recents : Tree = null
+
+#var files : Dictionary[StringName, Dictionary] = {}
+
+const CUTE_ICON : Texture2D = preload("res://addons/_Godot-IDE_/shared_resources/Script.svg")
+
+var _first_time : bool = false
+
+var _recents : PackedStringArray = []
+
+var _default_tx : Texture2D = null
+
+var _collapsed : bool = false
+
+func _enter_tree() -> void:
+ _first_time = true
+
+ var screen : Vector2 = DisplayServer.screen_get_size()
+ var value : Variant = IDE.get_config("fancy_search_class", "size")
+ if value is Vector2 or value is Vector2i:
+ screen = value
+ else:
+ screen = screen * 0.6
+
+ IDE.clamp_screen_size(screen, 0.3, 1.0)
+
+ size = screen
+
+func _ready() -> void:
+ update()
+ for x : int in range(1, _container.get_child_count(), 1):
+ _container.get_child(x).queue_free()
+ visibility_changed.connect(_on_visible)
+ _container.tab_changed.connect(_on_change)
+ _tree.item_activated.connect(_on_activate.bind(_tree))
+ _tree_recents.item_activated.connect(_on_activate.bind(_tree_recents))
+
+ var result : Variant = IDE.get_file_config_value("fancy_search_class", "recents")
+ if result is PackedStringArray:
+ _recents = result
+
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return
+ get_child(0). add_theme_stylebox_override(&"panel", control.get_theme_stylebox(&"panel", &""))
+
+func _save() -> void:
+ IDE.set_file_config_value("fancy_search_class", "recents", _recents)
+
+func _on_visible() -> void:
+ if visible:
+ update()
+ else:
+ _save()
+ queue_free()
+
+func clear() -> void:
+ _recents.clear()
+ _tree_recents.clear()
+
+func _on_activate(tree : Tree) -> void:
+ if !tree:
+ return
+
+ var item : TreeItem = tree.get_selected()
+
+ if !item:
+ return
+
+ var value : Variant = item.get_metadata(0)
+ if value is String:
+ if FileAccess.file_exists(value):
+ EditorInterface.select_file(value)
+ if ResourceLoader.exists(value):
+ var res : Resource = ResourceLoader.load(value)
+ if !(value in _recents):
+ while _recents.size() > 30:
+ _recents.remove_at(0)
+ _recents.append(value)
+ if res is Resource:
+ if res is PackedScene:
+ EditorInterface.open_scene_from_path(value)
+ elif res is Script:
+ EditorInterface.edit_script(res)
+ else:
+ EditorInterface.edit_resource(res)
+ hide()
+
+func _on_change(_tab_changed : int) -> void:
+ var control : Control = _container.get_current_tab_control()
+ if control:
+ _update_tree(control.name)
+
+func _exit_tree() -> void:
+ if is_instance_valid(_tree):
+ _tree.clear()
+ IDE.set_config("fancy_search_class", "size", size)
+
+func close() -> void:
+ hide()
+
+func get_icon(type : String) -> Texture2D:
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return null
+ return control.get_theme_icon(type, "EditorIcons")
+
+func _make_tree(root : TreeItem, key : StringName, dat : Dictionary, buff : Dictionary) -> void:
+ if buff.has(key):
+ return
+ var tree : TreeItem = root.create_child()
+ buff[key] = tree
+ tree.set_text(0, key)
+ for k : StringName in dat.keys():
+ var packed : PackedStringArray = dat["class"]
+ for p : String in packed:
+ var t : TreeItem = tree.create_child()
+ t.set_text(0, p)
+ buff[p] = t
+
+var pumpking : Pumpking = null
+
+class Pumpking extends RefCounted:
+ var cute_name : StringName = ""
+ var next : Array[Pumpking] = []
+ var back : Pumpking = null
+
+ var witchies : PackedStringArray = []
+
+ func born_pumpking() -> Pumpking:
+ var borned_pumpking : Pumpking = Pumpking.new()
+ next.append(borned_pumpking)
+ return borned_pumpking
+
+ func merge_pumpking(pumpking : Pumpking) -> void:
+ if !(next in next) and back != pumpking:
+ if pumpking in next:
+ return
+ next.append(pumpking)
+
+ func get_pumpking(check_cute_name : StringName) -> Pumpking:
+ if check_cute_name == cute_name:
+ return self
+
+ for x : Pumpking in next:
+ var y : Pumpking = x.get_pumpking(check_cute_name)
+ if y:
+ return y
+
+ return null
+
+func _update_tree(filter : StringName) -> void:
+ _tree.clear()
+ if pumpking:
+ if filter == &"All":
+ _collapsed = true
+ var root : TreeItem = _tree.create_item()
+ root.set_text(0, pumpking.cute_name)
+ for x : Pumpking in pumpking.next:
+ _fill_tree(root, x)
+ else:
+ _collapsed = false
+ var current : Pumpking = pumpking.get_pumpking(filter)
+ if current != pumpking:
+ var root : TreeItem = _tree.create_item()
+ root.set_text(0, current.cute_name)
+ _fill_tree(root, current)
+ else:
+ _update_tree(&"All")
+
+func _process(_delta: float) -> void:
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if !fs:
+ return
+ if fs.is_scanning():
+ return
+ set_process(false)
+ _update()
+
+func update() -> void:
+ set_process(true)
+
+func _update() -> void:
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if fs:
+ var fd : EditorFileSystemDirectory = fs.get_filesystem()
+ if fd:
+ for x : int in range(1, _container.get_child_count(), 1):
+ var node : Node = _container.get_child(x)
+ node.name = node.name + "_queue"
+ node.queue_free()
+
+ pumpking = Pumpking.new()
+ search(fd)
+
+ if _default_tx == null:
+ _default_tx = get_icon("DEFAULT_NOT_FOUND")
+
+ for p : Pumpking in pumpking.next:
+ _fill_tab(p)
+
+ if _first_time:
+ _first_time = false
+ _update_tree.call_deferred(&"All")
+ _update_recents()
+
+func _fill_tab(pump : Pumpking) -> void:
+ var control : Control = Control.new()
+ var index : int = -1
+ var nname : StringName = pump.cute_name
+ control.set_deferred(&"name", nname)
+ _container.add_child(control)
+ index = control.get_index()
+ if _container.get_tab_count() > index:
+ var icon : Texture2D = get_icon(nname)
+ if icon == _default_tx:
+ icon = CUTE_ICON
+ _container.set_tab_icon(index, icon)
+ for p : Pumpking in pump.next:
+ _fill_tab(p)
+
+func _fill_tree(root : TreeItem, pump : Pumpking) -> void:
+ var tree : TreeItem = root.create_child()
+ var nname : StringName = pump.cute_name
+ tree.set_text(0, nname)
+ var icon : Texture2D = get_icon(nname)
+ if icon == _default_tx:
+ icon = CUTE_ICON
+ tree.set_icon(0, icon)
+ tree.set_selectable(0, false)
+ tree.collapsed = _collapsed
+
+ for w : String in pump.witchies:
+ var witch : TreeItem = tree.create_child()
+
+ var text : String = w
+ var slice : int = w.get_slice_count("/")
+ if slice > 6:
+ text = w.get_file()
+ for x : int in range(slice - 1, slice - 4, -1):
+ text = w.get_slice("/", x).path_join(text)
+ text = "...".path_join(text)
+
+ witch.set_text(0, text)
+ witch.set_icon(0, icon)
+ witch.set_custom_color(0, Color.DARK_GRAY)
+ witch.set_icon_modulate(0, Color.DARK_GRAY)
+ witch.set_metadata(0, w)
+ witch.set_tooltip_text(0, w)
+
+ for x : Pumpking in pump.next:
+ _fill_tree(tree, x)
+
+func _update_recents() -> void:
+ _tree_recents.clear()
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if fs:
+ var data : Dictionary[String, PackedStringArray] = {}
+ var exist : PackedStringArray = []
+ for x : String in _recents:
+ if FileAccess.file_exists(x):
+ var type : StringName = "File"
+ var fe : EditorFileSystemDirectory = fs.get_filesystem_path(x.get_base_dir())
+ if fe:
+ for f : int in fe.get_file_count():
+ if fe.get_file_path(f) == x:
+ type = fe.get_file_script_class_name(f)
+ if type.is_empty():
+ type = fe.get_file_script_class_extends(f)
+ break
+ if type.is_empty():
+ type = fs.get_file_type(x)
+ if !data.has(type):
+ var packed : PackedStringArray = []
+ data[type] = packed
+ data[type].append(x)
+ exist.append(x)
+ _recents = exist
+
+
+ var root : TreeItem = _tree_recents.create_item()
+ root.set_text(0, "Recents Files")
+ root.set_selectable(0, false)
+ root.set_custom_color(0, Color.WHITE)
+
+ for k : String in data.keys():
+ var item : TreeItem = root.create_child()
+ var icon : Texture2D = get_icon(k)
+
+ item.set_text(0, k)
+ item.set_selectable(0, false)
+ item.set_custom_color(0, Color.GRAY)
+ item.set_icon_modulate(0, Color.GRAY)
+
+ if icon == _default_tx:
+ icon = CUTE_ICON
+ item.set_icon(0, icon)
+ for y : String in data[k]:
+ var fitem : TreeItem = item.create_child()
+ fitem.set_text(0, y.get_file())
+ fitem.set_icon(0, icon)
+ fitem.set_tooltip_text(0, y)
+ fitem.set_custom_color(0, Color.DARK_GRAY)
+ fitem.set_icon_modulate(0, Color.DARK_GRAY)
+ fitem.set_metadata(0, y)
+
+func search(fd : EditorFileSystemDirectory) -> void:
+ if pumpking == null:
+ pumpking = Pumpking.new()
+ pumpking.cute_name = "Found Files"
+ for f : int in fd.get_file_count():
+ var extension : StringName = fd.get_file_script_class_extends(f)
+ var type : StringName = fd.get_file_script_class_name(f)
+ var ee : bool = extension.is_empty()
+ var te : bool = type.is_empty()
+ if ee and te:
+ continue
+ var path : String = fd.get_file_path(f)
+
+ if !ee:
+ var grand_pumpking : Pumpking = pumpking.get_pumpking(extension)
+ if grand_pumpking == null:
+ grand_pumpking = pumpking.born_pumpking()
+ grand_pumpking.cute_name = extension
+ if !te:
+ var newer_pumpking : Pumpking = grand_pumpking.get_pumpking(type)
+ if newer_pumpking == null:
+ newer_pumpking = grand_pumpking.born_pumpking()
+ newer_pumpking.cute_name = type
+ newer_pumpking.witchies.append(path)
+ else:
+ grand_pumpking.witchies.append(path)
+ elif !te:
+ var new_pumpking : Pumpking = pumpking.get_pumpking(type)
+ if new_pumpking == null:
+ new_pumpking = pumpking.born_pumpking()
+ new_pumpking.cute_name = type
+ new_pumpking.witchies.append(path)
+
+ #if !ee:
+ #if !files.has(extension):
+ #var current_files : PackedStringArray = []
+ #var classes : PackedStringArray = []
+ #files[extension] = {
+ #"files" : current_files,
+ #"class" : classes,
+ #"parent" : ""
+ #}
+ #var data : Dictionary = files[extension]
+ #var packed : PackedStringArray = data["files"]
+ #
+ #if te:
+ #if !(path in packed):
+ #packed.append(path)
+ #
+ #if !te:
+ #if !files.has(type):
+ #var current_files : PackedStringArray = []
+ #var classes : PackedStringArray = []
+ #files[type] = {
+ #"files" : current_files,
+ #"class" : classes,
+ #"parent" : extension
+ #}
+ #var data : Dictionary = files[type]
+ #var packed : PackedStringArray = data["files"]
+ #if !(path in packed):
+ #packed.append(path)
+
+ for x : int in fd.get_subdir_count():
+ search(fd.get_subdir(x))
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd.uid
new file mode 100644
index 0000000..c01869e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd.uid
@@ -0,0 +1 @@
+uid://dalllwmyh2v5m
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.tscn b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.tscn
new file mode 100644
index 0000000..62279c4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.tscn
@@ -0,0 +1,126 @@
+[gd_scene load_steps=6 format=3 uid="uid://cfwi81ii6ww5b"]
+
+[ext_resource type="Script" uid="uid://dalllwmyh2v5m" path="res://addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.gd" id="1_v5iyo"]
+[ext_resource type="Script" uid="uid://c6pfvkgkabvt6" path="res://addons/_Godot-IDE_/plugins/fancy_search_class/gui/button.gd" id="2_bnpnb"]
+[ext_resource type="Texture2D" uid="uid://816fejewtbfj" path="res://addons/_Godot-IDE_/shared_resources/search.svg" id="2_nf02n"]
+[ext_resource type="Script" uid="uid://blmveofp20cm1" path="res://addons/_Godot-IDE_/plugins/fancy_search_class/gui/line_edit.gd" id="3_m1yal"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_nf02n"]
+content_margin_left = 5.0
+content_margin_top = 5.0
+content_margin_right = 5.0
+content_margin_bottom = 5.0
+bg_color = Color(0.1155, 0.132, 0.1595, 1)
+corner_detail = 1
+anti_aliasing = false
+
+[node name="Window" type="Popup" node_paths=PackedStringArray("_container", "_tree", "_tree_recents")]
+title = "Fancy Search Class"
+initial_position = 4
+size = Vector2i(816, 500)
+visible = true
+script = ExtResource("1_v5iyo")
+_container = NodePath("Main/MainContainer/HBoxContainer/BoxContainer/Container")
+_tree = NodePath("Main/MainContainer/HBoxContainer/BoxContainer/Resource/Tree")
+_tree_recents = NodePath("Main/MainContainer/HBoxContainer/RecentsContainer/SCroll/Recents")
+
+[node name="Main" type="PanelContainer" parent="."]
+custom_minimum_size = Vector2(200, 500)
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_nf02n")
+
+[node name="MainContainer" type="MarginContainer" parent="Main"]
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Main/MainContainer"]
+layout_mode = 2
+
+[node name="BoxContainer" type="VBoxContainer" parent="Main/MainContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="Tittle" type="Label" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+text = "Search Files By Class"
+horizontal_alignment = 1
+vertical_alignment = 1
+
+[node name="Separator" type="HSeparator" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+
+[node name="Container" type="TabContainer" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+current_tab = 0
+
+[node name="All" type="Control" parent="Main/MainContainer/HBoxContainer/BoxContainer/Container"]
+layout_mode = 2
+metadata/_tab_index = 0
+
+[node name="LineEdit" type="LineEdit" parent="Main/MainContainer/HBoxContainer/BoxContainer" node_paths=PackedStringArray("tree")]
+layout_mode = 2
+placeholder_text = "File Search"
+alignment = 1
+clear_button_enabled = true
+right_icon = ExtResource("2_nf02n")
+script = ExtResource("3_m1yal")
+tree = NodePath("../Resource/Tree")
+
+[node name="Resource" type="ScrollContainer" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="Tree" type="Tree" parent="Main/MainContainer/HBoxContainer/BoxContainer/Resource"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="Bottom" type="HSeparator" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+
+[node name="close" type="Button" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+text = "Close"
+script = ExtResource("2_bnpnb")
+
+[node name="RecentsContainer" type="VBoxContainer" parent="Main/MainContainer/HBoxContainer"]
+custom_minimum_size = Vector2(120, 0)
+layout_mode = 2
+
+[node name="Tittle" type="Label" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+text = "Recents"
+horizontal_alignment = 1
+
+[node name="Sep" type="HSeparator" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+
+[node name="LineEdit2" type="LineEdit" parent="Main/MainContainer/HBoxContainer/RecentsContainer" node_paths=PackedStringArray("tree")]
+layout_mode = 2
+placeholder_text = "Recent Search"
+alignment = 1
+clear_button_enabled = true
+right_icon = ExtResource("2_nf02n")
+script = ExtResource("3_m1yal")
+tree = NodePath("../SCroll/Recents")
+
+[node name="SCroll" type="ScrollContainer" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="Recents" type="Tree" parent="Main/MainContainer/HBoxContainer/RecentsContainer/SCroll"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="clear" type="Button" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+text = "Clear"
+script = ExtResource("2_bnpnb")
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.cfg b/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.cfg
new file mode 100644
index 0000000..ff87303
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="Fancy Search Class"
+description="Allow easy search class files"
+author="Twister"
+version=""
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd b/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd
new file mode 100644
index 0000000..400c066
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd
@@ -0,0 +1,33 @@
+@tool
+extends EditorPlugin
+# =============================================================================
+# Author: Twister
+# Fancy Search Class
+#
+# Addon for Godot
+# =============================================================================
+
+
+const FANCY_SEARCH : PackedScene = preload("res://addons/_Godot-IDE_/plugins/fancy_search_class/gui/main.tscn")
+
+var pop : Window = null
+var _c_input : InputEvent = null
+
+func _init() -> void:
+ var input : Variant = IDE.get_config("fancy_search_class", "invoke_input")
+ if input is InputEvent:
+ _c_input = input
+ else:
+ _c_input = InputEventKey.new()
+ _c_input.pressed = true
+ _c_input.alt_pressed = true
+ _c_input.keycode = KEY_DELETE
+ IDE.set_config("fancy_search_class", "invoke_input", _c_input)
+
+
+func _input(event: InputEvent) -> void:
+ if event.is_pressed() and event.is_match(_c_input):
+ if !is_instance_valid(pop):
+ pop = FANCY_SEARCH.instantiate()
+ add_child(pop)
+ pop.popup_centered()
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd.uid
new file mode 100644
index 0000000..a9c3fa5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_class/plugin.gd.uid
@@ -0,0 +1 @@
+uid://dhvs8xuxenk2y
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd
new file mode 100644
index 0000000..d428dd3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd
@@ -0,0 +1,13 @@
+@tool
+extends Button
+# =============================================================================
+# Author: Twister
+# Fancy Search Files
+#
+# Addon for Godot
+# =============================================================================
+
+
+func _pressed() -> void:
+ if owner.has_method(name):
+ owner.call(name)
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd.uid
new file mode 100644
index 0000000..aeb4b02
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd.uid
@@ -0,0 +1 @@
+uid://cpdqmdfvgpxmv
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd
new file mode 100644
index 0000000..6e91f1d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd
@@ -0,0 +1,66 @@
+@tool
+extends LineEdit
+# =============================================================================
+# Author: Twister
+# Fancy Search Files
+#
+# Addon for Godot
+# =============================================================================
+
+
+@export var tree : Tree = null
+
+func _ready() -> void:
+ text_changed.connect(sxrch)
+
+func reset() -> void:
+ if tree:
+ var root : TreeItem = tree.get_root()
+ if !root:
+ return
+ _reset(root)
+
+func _reset(root : TreeItem) -> void:
+ root.visible = true
+ for c : TreeItem in root.get_children():
+ _reset(c)
+
+func sxrch(txt : String) -> void:
+ reset()
+ if txt.is_empty():
+ return
+ if tree:
+ var root : TreeItem = tree.get_root()
+ if !root:
+ return
+ var rgx0 : RegEx = RegEx.create_from_string("(?i)\\b{0}\\b".format([txt]))
+ var rgx1 : RegEx = RegEx.create_from_string("(?i).*{0}.*".format([txt]))
+ var d0 : Array[TreeItem] = []
+ var d1 : Array[TreeItem] = []
+
+ for x : RegEx in [rgx0, rgx1]:
+ if !is_instance_valid(x) or !x.is_valid():
+ return
+
+ _sxrch(root, rgx0, rgx1, d0, d1)
+ root.visible = false
+
+ for t : TreeItem in d0:
+ _visible(t)
+ for t : TreeItem in d1:
+ _visible(t)
+
+func _visible(root : TreeItem) -> void:
+ if root:
+ root.visible = true
+ _visible(root.get_parent())
+
+func _sxrch(root : TreeItem, rgx0 : RegEx, rgx1 : RegEx, d0 : Array[TreeItem], d1 : Array[TreeItem]) -> void:
+ var txt : String = root.get_text(0)
+ root.visible = false
+ if rgx0.search(txt) != null:
+ d0.append(root)
+ elif d0.size() == 0 and rgx1.search(txt) != null:
+ d1.append(root)
+ for x : TreeItem in root.get_children():
+ _sxrch(x, rgx0, rgx1, d0, d1)
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd.uid
new file mode 100644
index 0000000..ecbaf5e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd.uid
@@ -0,0 +1 @@
+uid://bx6ivdaldbcf8
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd
new file mode 100644
index 0000000..00f0ce9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd
@@ -0,0 +1,253 @@
+@tool
+extends Window
+# =============================================================================
+# Author: Twister
+# Fancy Search Files
+#
+# Addon for Godot
+# =============================================================================
+
+
+@export var _container : TabContainer = null
+@export var _tree : Tree = null
+@export var _tree_recents : Tree = null
+
+var files : Dictionary[StringName, PackedStringArray] = {}
+
+var _first_time : bool = false
+
+var _recents : PackedStringArray = []
+
+var _default_tx : Texture2D = null
+
+func _enter_tree() -> void:
+ _first_time = true
+
+ var screen : Vector2 = DisplayServer.screen_get_size()
+ var value : Variant = IDE.get_config("fancy_search_files", "size")
+ if value is Vector2 or value is Vector2i:
+ screen = value
+ else:
+ screen = screen * 0.6
+ IDE.clamp_screen_size(screen, 0.3, 1.0)
+ size = screen
+
+func _ready() -> void:
+ update()
+ for x : int in range(1, _container.get_child_count(), 1):
+ _container.get_child(x).queue_free()
+ visibility_changed.connect(_on_visible)
+ _container.tab_changed.connect(_on_change)
+ _tree.item_activated.connect(_on_activate.bind(_tree))
+ _tree_recents.item_activated.connect(_on_activate.bind(_tree_recents))
+
+ var result : Variant = IDE.get_file_config_value("fancy_search_files", "recents")
+ if result is PackedStringArray:
+ _recents = result
+
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return
+ get_child(0). add_theme_stylebox_override(&"panel", control.get_theme_stylebox(&"panel", &""))
+
+func _save() -> void:
+ IDE.set_file_config_value("fancy_search_files", "recents", _recents)
+
+func _on_visible() -> void:
+ if visible:
+ update()
+ else:
+ _save()
+ queue_free()
+
+func clear() -> void:
+ _recents.clear()
+ _tree_recents.clear()
+
+func _on_activate(tree : Tree) -> void:
+ if !tree:
+ return
+
+ var item : TreeItem = tree.get_selected()
+
+ if !item:
+ return
+
+ var value : Variant = item.get_metadata(0)
+ if value is String:
+ if FileAccess.file_exists(value):
+ EditorInterface.select_file(value)
+ if ResourceLoader.exists(value):
+ var res : Resource = ResourceLoader.load(value)
+ if !(value in _recents):
+ while _recents.size() > 30:
+ _recents.remove_at(0)
+ _recents.append(value)
+ if res is Resource:
+ if res is PackedScene:
+ EditorInterface.open_scene_from_path(value)
+ elif res is Script:
+ EditorInterface.edit_script(res)
+ else:
+ EditorInterface.edit_resource(res)
+ hide()
+
+func _on_change(_tab_changed : int) -> void:
+ var control : Control = _container.get_current_tab_control()
+ if control:
+ _update_tree(control.name)
+
+func _exit_tree() -> void:
+ if is_instance_valid(_tree):
+ _tree.clear()
+ IDE.set_config("fancy_search_files", "size", size)
+
+func close() -> void:
+ hide()
+
+func get_icon(type : String) -> Texture2D:
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return null
+ var icon : Texture2D = control.get_theme_icon(type, "EditorIcons")
+
+ if icon == _default_tx:
+ icon = control.get_theme_icon("File", "EditorIcons")
+ return icon
+
+func _update_tree(filter : StringName) -> void:
+ _tree.clear()
+ if files.size() == 0:
+ return
+
+ if filter == &"All":
+ var root : TreeItem = _tree.create_item()
+ root.set_selectable(0, false)
+ for k : StringName in files.keys():
+ var item : TreeItem = root.create_child()
+ var icon : Texture2D = get_icon(k)
+ item.set_text(0, k)
+ item.set_icon_modulate(0, Color.WHITE)
+ item.set_selectable(0, false)
+ item.collapsed = true
+ item.set_icon(0, icon)
+ for f : String in files[k]:
+ var fitem : TreeItem = item.create_child()
+ var slice : int = f.get_slice_count("/")
+ if slice > 5:
+ var txt : String = ""
+ for x : int in range(slice - 1, 3, -1):
+ txt = f.get_slice("/", x).path_join(txt)
+ txt = "...".path_join(txt)
+ fitem.set_text(0, txt)
+ else:
+ fitem.set_text(0, f)
+ fitem.set_icon(0, icon)
+ fitem.set_metadata(0, f)
+ fitem.set_icon_modulate(0, Color.GRAY)
+ fitem.set_custom_color(0, Color.GRAY)
+ fitem.set_tooltip_text(0, f)
+ elif files.has(filter):
+ var root : TreeItem = _tree.create_item()
+ var item : TreeItem = root.create_child()
+ var icon : Texture2D = get_icon(filter)
+ item.set_text(0, filter)
+ item.set_icon_modulate(0, Color.WHITE)
+ item.set_selectable(0, false)
+ item.collapsed = false
+ item.set_icon(0, icon)
+ for f : String in files[filter]:
+ var fitem : TreeItem = item.create_child()
+ fitem.set_text(0, f.get_file())
+ fitem.set_icon(0, icon)
+ fitem.set_metadata(0, f)
+ fitem.set_icon_modulate(0, Color.GRAY)
+ fitem.set_custom_color(0, Color.GRAY)
+ fitem.set_tooltip_text(0, f)
+ else:
+ push_warning("Not valid type!")
+
+func _process(_delta: float) -> void:
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if !fs:
+ return
+ if fs.is_scanning():
+ return
+ set_process(false)
+ _update()
+
+func update() -> void:
+ set_process(true)
+
+func _update() -> void:
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if fs:
+ var fd : EditorFileSystemDirectory = fs.get_filesystem()
+ if fd:
+ for x : int in range(1, _container.get_child_count(), 1):
+ var node : Node = _container.get_child(x)
+ node.name = node.name + "_queue"
+ node.queue_free()
+ files.clear()
+ search(fd)
+
+ if _default_tx == null:
+ _default_tx = get_icon("DEFAULT_NOT_FOUND")
+
+ for x : StringName in files.keys():
+ var control : Control = Control.new()
+ var index : int = -1
+ control.set_deferred(&"name", x)
+ _container.add_child(control)
+ index = control.get_index()
+ if _container.get_tab_count() > index:
+ _container.set_tab_icon(index, get_icon(x))
+ if _first_time:
+ _first_time = false
+ _update_tree.call_deferred(&"All")
+ _update_recents()
+
+func _update_recents() -> void:
+ _tree_recents.clear()
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if fs:
+ var data : Dictionary[String, PackedStringArray] = {}
+ for x : String in _recents:
+ if FileAccess.file_exists(x):
+ var type : String = fs.get_file_type(x)
+ if !data.has(type):
+ var packed : PackedStringArray = []
+ data[type] = packed
+ data[type].append(x)
+
+ var root : TreeItem = _tree_recents.create_item()
+ root.set_text(0, "Recents Files")
+ root.set_selectable(0, false)
+ root.set_custom_color(0, Color.WHITE)
+ for k : String in data.keys():
+ var item : TreeItem = root.create_child()
+ var icon : Texture2D = get_icon(k)
+ item.set_text(0, k)
+ item.set_icon(0, icon)
+ item.set_selectable(0, false)
+ item.set_custom_color(0, Color.GRAY)
+ item.set_icon_modulate(0, Color.GRAY)
+ for y : String in data[k]:
+ var fitem : TreeItem = item.create_child()
+ fitem.set_text(0, y.get_file())
+ fitem.set_icon(0, icon)
+ fitem.set_tooltip_text(0, y)
+ fitem.set_custom_color(0, Color.DARK_GRAY)
+ fitem.set_icon_modulate(0, Color.DARK_GRAY)
+ fitem.set_metadata(0, y)
+
+func search(fd : EditorFileSystemDirectory) -> void:
+ for f : int in fd.get_file_count():
+ var type : StringName = fd.get_file_type(f)
+ if !files.has(type):
+ var _packed : PackedStringArray = []
+ files[type] = _packed
+ files[type].append(fd.get_file_path(f))
+
+ for x : int in fd.get_subdir_count():
+ search(fd.get_subdir(x))
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd.uid
new file mode 100644
index 0000000..f4aae77
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd.uid
@@ -0,0 +1 @@
+uid://dvsbvurbdecex
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.tscn b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.tscn
new file mode 100644
index 0000000..2bd06f4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.tscn
@@ -0,0 +1,126 @@
+[gd_scene load_steps=6 format=3 uid="uid://cddiai7iiut3l"]
+
+[ext_resource type="Script" uid="uid://dvsbvurbdecex" path="res://addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.gd" id="1_0f0lw"]
+[ext_resource type="Texture2D" uid="uid://816fejewtbfj" path="res://addons/_Godot-IDE_/shared_resources/search.svg" id="2_fowvl"]
+[ext_resource type="Script" uid="uid://cpdqmdfvgpxmv" path="res://addons/_Godot-IDE_/plugins/fancy_search_files/gui/button.gd" id="2_ydlx0"]
+[ext_resource type="Script" uid="uid://bx6ivdaldbcf8" path="res://addons/_Godot-IDE_/plugins/fancy_search_files/gui/line_edit.gd" id="3_5ntm4"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fowvl"]
+content_margin_left = 3.0
+content_margin_top = 3.0
+content_margin_right = 3.0
+content_margin_bottom = 3.0
+bg_color = Color(0.1155, 0.132, 0.1595, 1)
+corner_detail = 1
+anti_aliasing = false
+
+[node name="Window" type="Popup" node_paths=PackedStringArray("_container", "_tree", "_tree_recents")]
+title = "Fancy Search Files"
+initial_position = 4
+size = Vector2i(816, 500)
+visible = true
+script = ExtResource("1_0f0lw")
+_container = NodePath("Main/MainContainer/HBoxContainer/BoxContainer/Container")
+_tree = NodePath("Main/MainContainer/HBoxContainer/BoxContainer/Resource/Tree")
+_tree_recents = NodePath("Main/MainContainer/HBoxContainer/RecentsContainer/SCroll/Recents")
+
+[node name="Main" type="PanelContainer" parent="."]
+custom_minimum_size = Vector2(200, 500)
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_fowvl")
+
+[node name="MainContainer" type="MarginContainer" parent="Main"]
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Main/MainContainer"]
+layout_mode = 2
+
+[node name="BoxContainer" type="VBoxContainer" parent="Main/MainContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="Tittle" type="Label" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+text = "Search Files By Type"
+horizontal_alignment = 1
+vertical_alignment = 1
+
+[node name="Separator" type="HSeparator" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+
+[node name="Container" type="TabContainer" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+current_tab = 0
+
+[node name="All" type="Control" parent="Main/MainContainer/HBoxContainer/BoxContainer/Container"]
+layout_mode = 2
+metadata/_tab_index = 0
+
+[node name="LineEdit" type="LineEdit" parent="Main/MainContainer/HBoxContainer/BoxContainer" node_paths=PackedStringArray("tree")]
+layout_mode = 2
+placeholder_text = "File Search"
+alignment = 1
+clear_button_enabled = true
+right_icon = ExtResource("2_fowvl")
+script = ExtResource("3_5ntm4")
+tree = NodePath("../Resource/Tree")
+
+[node name="Resource" type="ScrollContainer" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="Tree" type="Tree" parent="Main/MainContainer/HBoxContainer/BoxContainer/Resource"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="Bottom" type="HSeparator" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+
+[node name="close" type="Button" parent="Main/MainContainer/HBoxContainer/BoxContainer"]
+layout_mode = 2
+text = "Close"
+script = ExtResource("2_ydlx0")
+
+[node name="RecentsContainer" type="VBoxContainer" parent="Main/MainContainer/HBoxContainer"]
+custom_minimum_size = Vector2(120, 0)
+layout_mode = 2
+
+[node name="Tittle" type="Label" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+text = "Recents"
+horizontal_alignment = 1
+
+[node name="Sep" type="HSeparator" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+
+[node name="LineEdit2" type="LineEdit" parent="Main/MainContainer/HBoxContainer/RecentsContainer" node_paths=PackedStringArray("tree")]
+layout_mode = 2
+placeholder_text = "Recent Search"
+alignment = 1
+clear_button_enabled = true
+right_icon = ExtResource("2_fowvl")
+script = ExtResource("3_5ntm4")
+tree = NodePath("../SCroll/Recents")
+
+[node name="SCroll" type="ScrollContainer" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="Recents" type="Tree" parent="Main/MainContainer/HBoxContainer/RecentsContainer/SCroll"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="clear" type="Button" parent="Main/MainContainer/HBoxContainer/RecentsContainer"]
+layout_mode = 2
+text = "Clear"
+script = ExtResource("2_ydlx0")
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.cfg b/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.cfg
new file mode 100644
index 0000000..4bc7638
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="Fancy Search Files"
+description="Allow easy search files by type"
+author="Twister"
+version=""
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd b/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd
new file mode 100644
index 0000000..bb35d1a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd
@@ -0,0 +1,33 @@
+@tool
+extends EditorPlugin
+# =============================================================================
+# Author: Twister
+# Fancy Search Files
+#
+# Addon for Godot
+# =============================================================================
+
+const FANCY_SEARCH : PackedScene = preload("res://addons/_Godot-IDE_/plugins/fancy_search_files/gui/main.tscn")
+
+var pop : Window = null
+
+var _c_input : InputEvent = null
+
+func _init() -> void:
+ var input : Variant = IDE.get_config("fancy_search_files", "invoke_input")
+ if input is InputEvent:
+ _c_input = input
+ else:
+ _c_input = InputEventKey.new()
+ _c_input.pressed = true
+ _c_input.ctrl_pressed = true
+ _c_input.alt_pressed = true
+ _c_input.keycode = KEY_SPACE
+ IDE.set_config("fancy_search_files", "invoke_input", _c_input)
+
+func _input(event: InputEvent) -> void:
+ if event.is_pressed() and event.is_match(_c_input):
+ if !is_instance_valid(pop):
+ pop = FANCY_SEARCH.instantiate()
+ add_child(pop)
+ pop.popup_centered()
diff --git a/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd.uid b/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd.uid
new file mode 100644
index 0000000..a779cfc
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/fancy_search_files/plugin.gd.uid
@@ -0,0 +1 @@
+uid://8e7vxkrhsi2j
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.cfg b/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.cfg
new file mode 100644
index 0000000..dfbbe21
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="GDOverrideFunctions"
+description="Allow select virtual functions for override/implement."
+author="Twister"
+version="0.3"
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd b/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd
new file mode 100644
index 0000000..b33d2ec
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd
@@ -0,0 +1,54 @@
+@tool
+extends EditorPlugin
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# GD Override Functions
+#
+# Virtual Popups override functions. godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+var RES : Script = preload("res://addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd")
+
+#region extension_features
+var popup_virtual_functions : RefCounted = null
+var popup_virtual_functions_code : RefCounted = null
+#endregion
+
+var _c_input : InputEvent = null
+
+func _init() -> void:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ var input : Variant = editor.get_setting("plugin/gd_override_functions/invoke_input")
+ if input is InputEvent:
+ _c_input = input
+ else:
+ _c_input = InputEventKey.new()
+ _c_input.pressed = true
+ _c_input.alt_pressed = true
+ _c_input.keycode = KEY_INSERT
+ editor.set_setting("plugin/gd_override_functions/invoke_input", _c_input)
+
+
+func _enter_tree() -> void:
+ popup_virtual_functions = RES.new()
+ popup_virtual_functions_code = RES.new()
+ add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, popup_virtual_functions)
+ add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, popup_virtual_functions_code)
+
+func _exit_tree() -> void:
+ remove_context_menu_plugin(popup_virtual_functions)
+ remove_context_menu_plugin(popup_virtual_functions_code)
+
+ popup_virtual_functions = null
+ popup_virtual_functions_code = null
+
+#Input because the dev can be change buttons ( >.>)
+func _input(event: InputEvent) -> void:
+ if event.is_pressed() and event.is_match(_c_input, true):
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var sc : Script = editor.get_current_script()
+ if sc:
+ if popup_virtual_functions and popup_virtual_functions.has_method(&"callback"):
+ popup_virtual_functions_code.call(&"callback", sc)
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd.uid b/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd.uid
new file mode 100644
index 0000000..f485967
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/plugin.gd.uid
@@ -0,0 +1 @@
+uid://bo3i5csexlcoe
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/button/pressed.tres b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/button/pressed.tres
new file mode 100644
index 0000000..5d2a3d4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/button/pressed.tres
@@ -0,0 +1,14 @@
+[gd_resource type="StyleBoxFlat" format=3 uid="uid://daq1bri8pp6lp"]
+
+[resource]
+bg_color = Color(0.352941, 0.12549, 0.352941, 0.25098)
+border_width_left = 4
+border_width_top = 4
+border_width_right = 4
+border_width_bottom = 4
+border_color = Color(0.631373, 0.270588, 0.631373, 1)
+border_blend = true
+corner_radius_top_left = 4
+corner_radius_top_right = 4
+corner_radius_bottom_right = 4
+corner_radius_bottom_left = 4
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/icon/Attribute.txt b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/icon/Attribute.txt
new file mode 100644
index 0000000..7c791f7
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/icon/Attribute.txt
@@ -0,0 +1,2 @@
+ICONS
+https://www.flaticon.com/free-icons
\ No newline at end of file
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd
new file mode 100644
index 0000000..86d8db8
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd
@@ -0,0 +1,1203 @@
+@tool
+extends Popup
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# script-ide: Virtual Popups
+#
+# Virtual Popups for script-ide addon.godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const BUILT_IN_SCRIPT: String = "::GDScript"
+
+const ICON_PUBLIC : Texture = preload("res://addons/_Godot-IDE_/shared_resources/func_public.svg")
+const ICON_VIRTUALS : Texture = preload("res://addons/_Godot-IDE_/shared_resources/func_virtual.svg")
+const ICON_PRIVATE : Texture = preload("res://addons/_Godot-IDE_/shared_resources/func_private.svg")
+const ICON_CHECKED : Texture = preload("res://addons/_Godot-IDE_/shared_resources/check.svg")
+#const ICON_WARNING : Texture = preload("res://addons/gd_override_functions/popup/icon/warning.png")
+
+const ICON_NATIVE_CLASS : Texture = preload("res://addons/_Godot-IDE_/shared_resources/Script.svg")
+const ICON_CUSTOM_CLASS : Texture = preload("res://addons/_Godot-IDE_/shared_resources/ScriptExtend.svg")
+const ICON_CUSTOM_SCRIPT : Texture = preload("res://addons/_Godot-IDE_/shared_resources/PluginScript.svg")
+const ICON_INTERFACE_SCRIPT : Texture = preload("res://addons/_Godot-IDE_/shared_resources/InterfaceScript.svg")
+
+const ICON_ORDER_DEFAULT : Texture = preload("res://addons/_Godot-IDE_/shared_resources/up.svg")
+const ICON_ORDER_INVERT : Texture = preload("res://addons/_Godot-IDE_/shared_resources/down.svg")
+
+var COLOR_CLASS : Color = Color.DARK_SLATE_BLUE
+var COLOR_NATIVE_CLASS : Color = Color.BLACK
+var COLOR_PARAMETERS : Color = Color.BLACK
+var COLOR_INTERFACE : Color = Color.BLACK
+
+var include_paremeters : bool = false
+
+# FILTERS
+var _private_begin_equal_protected : bool = false
+var _public_filter : bool = false
+var _private_filter : bool = false
+var _protected_filter : bool = true
+var _interface_filter : bool = true
+
+enum FILTER_TYPE{
+ DEFAULT,
+ REVERSE,
+ DEFAUL_TREE,
+ REVERSE_TREE
+}
+
+@export_tool_button("Test")
+var test_button: Callable = _testing
+
+@export var tree : Tree
+@export var accept_button : Button
+@export var cancel_button : Button
+
+@export var check_generate_at_line : CheckBox
+
+# GENERATORS
+@export var interface_generate_button : Button
+@export var virtual_generate_button : Button
+
+#region order
+@export var order_button : Button
+#endregion
+
+#region filter_handler
+@export var public_button : Button
+@export var protected_button : Button
+@export var private_button : Button
+@export var interface_button : Button
+
+var _last_script : Script = null
+var _last_filter : FILTER_TYPE = FILTER_TYPE.REVERSE
+#endregion
+
+var _buffer_data : Dictionary = {}
+var _created_funcs : Dictionary = {}
+
+var _generate_at_end_line : bool = true
+
+#region _USER_CONFIG_
+## Class name chars in the begin for identify a class as interface
+var _interface_begins_with : String = "I"
+## Class name chars in the end for identify a class as interface.
+var _interface_end_with : String = "Interface"
+## Ignore Upper/Lower case of class name.
+var _interface_ignore_case : bool = true
+## Function name in the begin for identify function as virtual.
+var _char_virtual_function : String = "_"
+## Function name in the end for identify function as virtual.
+var _char_private_function : String = "__"
+## Include native class like IMyClass, MyClassInterface.
+var _include_native_class_for_check_interfaces : bool = false
+#endregion
+
+func _update_settings() -> void:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if null == editor:
+ return
+
+ if editor.has_setting("plugin/gd_override_functions/interface/class_as_interface_if_begins_with"):
+ _interface_begins_with = editor.get_setting("plugin/gd_override_functions/interface/class_as_interface_if_begins_with")
+ if editor.has_setting("plugin/gd_override_functions/interface/class_as_interface_if_end_with"):
+ _interface_end_with = editor.get_setting("plugin/gd_override_functions/interface/class_as_interface_if_end_with")
+ if editor.has_setting("plugin/gd_override_functions/interface/class_interface_name_ignore_case"):
+ _interface_ignore_case = editor.get_setting("plugin/gd_override_functions/interface/class_interface_name_ignore_case")
+ if editor.has_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with"):
+ _char_virtual_function = editor.get_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with")
+ if editor.has_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with"):
+ _char_private_function = editor.get_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with")
+ if editor.has_setting("plugin/gd_override_functions/inheritance/include_native_class_for_check_interfaces"):
+ _include_native_class_for_check_interfaces = editor.get_setting("plugin/gd_override_functions/inheritance/include_native_class_for_check_interfaces")
+ if _interface_ignore_case:
+ _interface_end_with = _interface_end_with.to_lower()
+ _interface_begins_with = _interface_begins_with.to_lower()
+
+func make_tree(input_script : Script, filter_type : FILTER_TYPE = _last_filter) -> void:
+ _buffer_data = {}
+ _created_funcs = {}
+ if tree == null:
+ push_error("Not defined tree!")
+ return
+
+ tree.clear()
+
+ _update_settings()
+
+ _last_script = input_script
+ _last_filter = filter_type
+
+ var callback : Callable = _on_accept_button
+ if accept_button:
+ if accept_button.pressed.is_connected(_on_accept_button):
+ accept_button.pressed.disconnect(_on_accept_button)
+ accept_button.pressed.connect(callback)
+ if tree.item_activated.is_connected(_on_accept_button):
+ tree.item_activated.disconnect(_on_accept_button)
+ tree.item_activated.connect(callback)
+
+ # script-ide: Check if built-in script. In this case we need to duplicate it for whatever reason.
+ if (input_script.get_path().contains(BUILT_IN_SCRIPT)):
+ input_script = input_script.duplicate()
+
+ var output : Array = generate_data(input_script)
+ var base : Dictionary = output[0]
+ var base_count : int = output[1]
+
+ #MAKE TREE
+ var start : int = 0
+ var end : int = base_count + 1
+ var step : int = 1
+ if filter_type == FILTER_TYPE.DEFAULT or filter_type == FILTER_TYPE.DEFAUL_TREE:
+ start = base_count
+ end = -1
+ step = -1
+
+ tree.set_column_custom_minimum_width(0, 25)
+
+ tree.set_column_title_alignment(0, HORIZONTAL_ALIGNMENT_CENTER)
+ tree.set_column_title_alignment(1, HORIZONTAL_ALIGNMENT_CENTER)
+ tree.set_column_title_alignment(2, HORIZONTAL_ALIGNMENT_CENTER)
+
+ tree.set_column_title(0, "Class/Functions")
+ tree.set_column_title(1, "Params")
+ tree.set_column_title(2, "Return")
+ tree.column_titles_visible = true
+
+ var root : TreeItem = tree.create_item()
+ root.set_text(0, "Classes")
+
+ _created_funcs = _clear_funcs(input_script)
+
+ _buffer_data = base
+
+ if filter_type == FILTER_TYPE.DEFAUL_TREE or filter_type == FILTER_TYPE.REVERSE_TREE:
+ var last : TreeItem = root
+ for x : int in range(start, end, step):
+ var dict : Dictionary = base[x]
+ var funcs : Dictionary = dict["funcs"]
+
+ if funcs.size() == 0:continue
+
+ var item : TreeItem = tree.create_item(last, -1)
+
+ item.set_text(0, dict["name"])
+ last = item
+
+ if dict["type"] == 0:
+ item.set_custom_bg_color(0, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(1, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(2, COLOR_NATIVE_CLASS)
+ item.collapsed = true
+ item.set_icon(0, ICON_NATIVE_CLASS)
+ elif dict["type"] == 1:
+ item.set_custom_bg_color(0, COLOR_CLASS)
+ item.set_custom_bg_color(1, COLOR_CLASS)
+ item.set_custom_bg_color(2, COLOR_CLASS)
+ if dict["custom"] == true:
+ item.set_icon(0, ICON_CUSTOM_SCRIPT)
+ else:
+ item.set_icon(0, ICON_CUSTOM_CLASS)
+ elif dict["type"] == 3:
+ item.set_custom_bg_color(0, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(1, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(2, COLOR_NATIVE_CLASS)
+ item.collapsed = true
+ item.set_icon(0, ICON_INTERFACE_SCRIPT)
+ else:
+ item.set_custom_bg_color(0, COLOR_INTERFACE)
+ item.set_custom_bg_color(1, COLOR_INTERFACE)
+ item.set_custom_bg_color(2, COLOR_INTERFACE)
+ if dict["custom"] == true:
+ item.set_icon(0, ICON_INTERFACE_SCRIPT)
+ else:
+ item.set_icon(0, ICON_INTERFACE_SCRIPT) #NOTNATIVE4NOW
+ item.set_selectable(0, false)
+ item.set_selectable(1, false)
+ item.set_selectable(2, false)
+ for key : Variant in funcs.keys():
+ var sub_item : TreeItem = tree.create_item(item, -1)
+ var func_name : PackedStringArray = (funcs[key] as String).split('||', false, 2)
+ for fx : int in range(0, func_name.size(), 1):
+ sub_item.set_text(fx, func_name[fx])
+ sub_item.set_text_alignment(1,HORIZONTAL_ALIGNMENT_CENTER)
+ sub_item.set_text_alignment(2,HORIZONTAL_ALIGNMENT_CENTER)
+ sub_item.set_selectable(1, false)
+ sub_item.set_selectable(2, false)
+ sub_item.set_custom_color(1, COLOR_PARAMETERS)
+ sub_item.set_custom_color(2, COLOR_PARAMETERS)
+ if _created_funcs.has(key):
+ sub_item.set_icon_overlay(0, ICON_CHECKED)
+ sub_item.set_selectable(0, false)
+ else:
+ #if dict["type"] == 2 and !override.has(key):
+ #sub_item.set_icon_overlay(0, ICON_WARNING)
+ #else:
+ sub_item.set_icon_overlay(0, null)
+ sub_item.set_selectable(0, true)
+ if (key as String).begins_with(_char_private_function):
+ sub_item.set_icon(0, ICON_PRIVATE)
+ elif (key as String).begins_with(_char_virtual_function):
+ sub_item.set_icon(0, ICON_VIRTUALS)
+ else:
+ sub_item.set_icon(0, ICON_PUBLIC)
+ else:
+ for x : int in range(start, end, step):
+ var dict : Dictionary = base[x]
+ var funcs : Dictionary = dict["funcs"]
+
+ if funcs.size() == 0:continue
+
+ var item : TreeItem = tree.create_item(null, -1)
+
+ item.set_text(0, dict["name"])
+ if dict["type"] == 0:
+ item.set_custom_bg_color(0, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(1, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(2, COLOR_NATIVE_CLASS)
+ item.collapsed = true
+ item.set_icon(0, ICON_NATIVE_CLASS)
+ elif dict["type"] == 1:
+ item.set_custom_bg_color(0, COLOR_CLASS)
+ item.set_custom_bg_color(1, COLOR_CLASS)
+ item.set_custom_bg_color(2, COLOR_CLASS)
+ if dict["custom"] == true:
+ item.set_icon(0, ICON_CUSTOM_SCRIPT)
+ else:
+ item.set_icon(0, ICON_CUSTOM_CLASS)
+ elif dict["type"] == 3:
+ item.set_custom_bg_color(0, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(1, COLOR_NATIVE_CLASS)
+ item.set_custom_bg_color(2, COLOR_NATIVE_CLASS)
+ item.collapsed = true
+ item.set_icon(0, ICON_INTERFACE_SCRIPT)
+ else:
+ item.set_custom_bg_color(0, COLOR_INTERFACE)
+ item.set_custom_bg_color(1, COLOR_INTERFACE)
+ item.set_custom_bg_color(2, COLOR_INTERFACE)
+ if dict["custom"] == true:
+ item.set_icon(0, ICON_INTERFACE_SCRIPT)
+ else:
+ item.set_icon(0, ICON_INTERFACE_SCRIPT) #NOTNATIVE4NOW
+ item.set_selectable(0, false)
+ item.set_selectable(1, false)
+ item.set_selectable(2, false)
+ for key : Variant in funcs.keys():
+ var sub_item : TreeItem = tree.create_item(item, -1)
+ var func_name : PackedStringArray = (funcs[key] as String).split('||', false, 2)
+ for fx : int in range(0, func_name.size(), 1):
+ sub_item.set_text(fx, func_name[fx])
+ sub_item.set_text_alignment(1,HORIZONTAL_ALIGNMENT_CENTER)
+ sub_item.set_text_alignment(2,HORIZONTAL_ALIGNMENT_CENTER)
+ sub_item.set_selectable(1, false)
+ sub_item.set_selectable(2, false)
+ sub_item.set_custom_color(1, COLOR_PARAMETERS)
+ sub_item.set_custom_color(2, COLOR_PARAMETERS)
+ if _created_funcs.has(key):
+ sub_item.set_icon_overlay(0, ICON_CHECKED)
+ sub_item.set_selectable(0, false)
+ else:
+ #if dict["type"] == 2 and !override.has(key):
+ #sub_item.set_icon_overlay(0, ICON_WARNING)
+ #else:
+ sub_item.set_icon_overlay(0, null)
+ sub_item.set_selectable(0, true)
+ if (key as String).begins_with(_char_private_function):
+ sub_item.set_icon(0, ICON_PRIVATE)
+ elif (key as String).begins_with(_char_virtual_function):
+ sub_item.set_icon(0, ICON_VIRTUALS)
+ else:
+ sub_item.set_icon(0, ICON_PUBLIC)
+
+ if root.get_child_count() == 0:
+ root.set_text(0, "No functions aviables!")
+ tree.hide_root = false
+
+ _update_gui()
+
+## Generate tree data, @output Array(base class data, total bases inherited class])
+func generate_data(script : Script) -> Array:
+ var data_base : Dictionary = {}
+ var base_count : int = _generate_native(script.get_instance_base_type(), data_base, _generate(script.get_base_script(), data_base))
+ return [data_base, base_count]
+
+func _on_settings_change() -> void:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = editor.get_changed_settings()
+ if "plugin/gd_override_functions/generate_at_end_line" in changes:
+ _generate_at_end_line = editor.get_setting("plugin/gd_override_functions/generate_at_end_line")
+ if "plugin/gd_override_functions/order_inverted" in changes:
+ var inverted : bool = editor.get_setting("plugin/gd_override_functions/order_inverted")
+ if inverted:
+ _last_filter = FILTER_TYPE.REVERSE
+ else:
+ _last_filter = FILTER_TYPE.DEFAULT
+
+ if "plugin/gd_override_functions/initial_size" in changes:
+ var _size : Variant = editor.get_setting("plugin/gd_override_functions/initial_size")
+ if _size is Vector2 or _size is Vector2i:
+ size = _size
+ size.x = maxf(size.x, 512.0)
+ size.y = maxf(size.y, 512.0)
+
+#region init
+func _ready() -> void:
+ var w_size : Vector2 = DisplayServer.window_get_size()
+ if w_size != Vector2.ZERO:
+ size = w_size * 0.6
+
+ config_update(false)
+
+ if !Engine.is_editor_hint():
+ #Component created for be used in editor mode, so testing is invoke in non editor mode.
+ _testing()
+
+
+func _testing() -> void:
+ await get_tree().process_frame
+
+ #Also work with class_name
+ var input_script : Script = ResourceLoader.load("res://addons/gd_override_functions/popup/testing/child.gd")
+
+ #Show popup
+ call_deferred(&"show")
+ make_tree(input_script, _last_filter)
+
+func _on_change_order_pressed() -> void:
+ match _last_filter:
+ #FILTER_TYPE.REVERSE:
+ #_last_filter = FILTER_TYPE.DEFAULT
+ #FILTER_TYPE.DEFAULT:
+ #_last_filter = FILTER_TYPE.REVERSE_TREE
+ #FILTER_TYPE.REVERSE_TREE:
+ ##_last_filter = FILTER_TYPE.DEFAUL_TREE
+ #FILTER_TYPE.DEFAUL_TREE:
+ #_last_filter = FILTER_TYPE.REVERSE
+ FILTER_TYPE.REVERSE:
+ _last_filter = FILTER_TYPE.DEFAULT
+ FILTER_TYPE.DEFAULT:
+ _last_filter = FILTER_TYPE.REVERSE
+ _:
+ _last_filter = FILTER_TYPE.REVERSE
+ var root : TreeItem = tree.get_root()
+ var collapsed : Dictionary = {}
+
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ collapsed[tree_item.get_text(0)] = tree_item.collapsed
+ tree_item = tree_item.get_next()
+
+ make_tree(_last_script, _last_filter)
+ root = tree.get_root()
+
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ var txt : String = str(tree_item.get_text(0))
+ if collapsed.has(txt):
+ tree_item.collapsed = collapsed[txt]
+ tree_item = tree_item.get_next()
+
+
+func _on_public_filter_pressed() -> void:
+ var root : TreeItem = tree.get_root()
+ var collapsed : Dictionary = {}
+
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ collapsed[tree_item.get_text(0)] = tree_item.collapsed
+ tree_item = tree_item.get_next()
+
+ _public_filter = !_public_filter
+ make_tree(_last_script, _last_filter)
+
+ root = tree.get_root()
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ var txt : String = str(tree_item.get_text(0))
+ if collapsed.has(txt):
+ tree_item.collapsed = collapsed[txt]
+ tree_item = tree_item.get_next()
+
+func _on_protected_filter_pressed() -> void:
+ var root : TreeItem = tree.get_root()
+ var collapsed : Dictionary = {}
+
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ collapsed[tree_item.get_text(0)] = tree_item.collapsed
+ tree_item = tree_item.get_next()
+
+ _protected_filter = !_protected_filter
+ make_tree(_last_script, _last_filter)
+
+ root = tree.get_root()
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ var txt : String = str(tree_item.get_text(0))
+ if collapsed.has(txt):
+ tree_item.collapsed = collapsed[txt]
+ tree_item = tree_item.get_next()
+
+func _on_private_filter_pressed() -> void:
+ var root : TreeItem = tree.get_root()
+ var collapsed : Dictionary = {}
+
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ collapsed[tree_item.get_text(0)] = tree_item.collapsed
+ tree_item = tree_item.get_next()
+
+ _private_filter = !_private_filter
+ make_tree(_last_script, _last_filter)
+
+ root = tree.get_root()
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ var txt : String = str(tree_item.get_text(0))
+ if collapsed.has(txt):
+ tree_item.collapsed = collapsed[txt]
+ tree_item = tree_item.get_next()
+
+func _on_interface_filter_pressed() -> void:
+ var root : TreeItem = tree.get_root()
+ var collapsed : Dictionary = {}
+
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ collapsed[tree_item.get_text(0)] = tree_item.collapsed
+ tree_item = tree_item.get_next()
+
+ _interface_filter = !_interface_filter
+ make_tree(_last_script, _last_filter)
+
+ root = tree.get_root()
+ if root:
+ var tree_item : TreeItem = root.get_first_child()
+ while null != tree_item:
+ var txt : String = str(tree_item.get_text(0))
+ if collapsed.has(txt):
+ tree_item.collapsed = collapsed[txt]
+ tree_item = tree_item.get_next()
+
+
+func _on_generate_virtual_pressed() -> void:
+ if _buffer_data.size() == 0:
+ print("Not class aviables!")
+ return
+ var funcs : Dictionary = {}
+ for x : Variant in _buffer_data.keys():
+ if _buffer_data[x]["type"] > 0:
+ var _class_data : Dictionary = _buffer_data[x]
+ var _funcs : Dictionary = _class_data["funcs"]
+ for _func : Variant in _funcs.keys():
+ var func_name : String = str(_func)
+ if !func_name.begins_with(_char_private_function) and func_name.begins_with(_char_virtual_function):
+ if _created_funcs.has(_func):
+ continue
+ funcs[_func] = {
+ "type" : _class_data["type"]
+ ,"class" :_class_data["name"]
+ ,"name" : str(_func)
+ }
+ if funcs.size() == 0:
+ print("Not has virtual methods for override/implement!")
+ return
+ _make(funcs)
+
+func _on_generate_interface_pressed() -> void:
+ if _buffer_data.size() == 0:
+ print("Not class aviables!")
+ return
+ var funcs : Dictionary = {}
+ for x : Variant in _buffer_data.keys():
+ if _buffer_data[x]["type"] == 2:
+ var _class_data : Dictionary = _buffer_data[x]
+ var _funcs : Dictionary = _class_data["funcs"]
+ for _func : Variant in _funcs.keys():
+ if _created_funcs.has(_func):
+ continue
+ funcs[_func] = {
+ "type" : _class_data["type"]
+ ,"class" :_class_data["name"]
+ ,"name" : str(_func)
+ }
+ continue
+ if funcs.size() == 0:
+ print("Not has interfaces methods for override/implement!")
+ return
+ _make(funcs)
+
+func _on_check_generate_at_line(toggled : bool) -> void:
+ _generate_at_end_line = toggled
+
+func config_update(save : bool = false) -> void:
+ const SETTING : String = "plugin/gd_override_functions/initial_size"
+ const SETTING_CHECK : String = "plugin/gd_override_functions/save_size_on_exit"
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+
+ size.x = maxf(size.x, 512)
+ size.y = maxf(size.y, 512)
+
+ if editor:
+ if !editor.has_setting(SETTING_CHECK):
+ editor.set_setting(SETTING_CHECK, true)
+
+ if !editor.has_setting(SETTING):
+ editor.set_setting(SETTING, (size as Vector2i))
+ return
+
+ if save:
+ if editor.get_setting(SETTING_CHECK) == true:
+ editor.set_setting(SETTING, (size as Vector2i))
+ else:
+ size = editor.get_setting(SETTING)
+
+func _init() -> void:
+ _private_begin_equal_protected = _char_private_function.begins_with(_char_virtual_function)
+ if !is_node_ready():
+ await ready
+ assert(tree and accept_button and cancel_button)
+
+ tree.select_mode = Tree.SELECT_MULTI
+ tree.multi_selected.connect(_on_tree_multi_selected)
+ cancel_button.pressed.connect(_on_cancel_button)
+
+ if public_button:
+ public_button.pressed.connect(_on_public_filter_pressed)
+ if protected_button:
+ protected_button.pressed.connect(_on_protected_filter_pressed)
+ if private_button:
+ private_button.pressed.connect(_on_private_filter_pressed)
+ if interface_button:
+ interface_button.pressed.connect(_on_interface_filter_pressed)
+ if interface_generate_button:
+ interface_generate_button.pressed.connect(_on_generate_interface_pressed)
+ if virtual_generate_button:
+ virtual_generate_button.pressed.connect(_on_generate_virtual_pressed)
+
+ if order_button:
+ order_button.pressed.connect(_on_change_order_pressed)
+
+ if check_generate_at_line:
+ check_generate_at_line.toggled.connect(_on_check_generate_at_line)
+
+ COLOR_CLASS = COLOR_CLASS.darkened(0.4)
+ COLOR_NATIVE_CLASS = COLOR_CLASS.darkened(0.4)
+ COLOR_PARAMETERS = COLOR_CLASS.lightened(0.3)
+ COLOR_INTERFACE = COLOR_CLASS.lightened(0.2)
+
+ visibility_changed.connect(_on_change_visibility)
+
+
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if !editor.has_setting("plugin/gd_override_functions/generate_at_end_line"):
+ editor.set_setting("plugin/gd_override_functions/generate_at_end_line", _generate_at_end_line)
+ editor.add_property_info({
+ "name": "plugin/gd_override_functions/generate_at_end_line",
+ "type" : TYPE_BOOL
+ })
+ else:
+ _generate_at_end_line = editor.get_setting("plugin/gd_override_functions/generate_at_end_line")
+
+ if !editor.has_setting("plugin/gd_override_functions/order_inverted"):
+ editor.set_setting("plugin/gd_override_functions/order_inverted", _last_filter == FILTER_TYPE.REVERSE)
+ editor.add_property_info({
+ "name": "plugin/gd_override_functions/order_inverted",
+ "type" : TYPE_BOOL
+ })
+ else:
+ var inverted : bool = editor.get_setting("plugin/gd_override_functions/order_inverted")
+ if inverted:
+ _last_filter = FILTER_TYPE.REVERSE
+ else:
+ _last_filter = FILTER_TYPE.DEFAULT
+
+ if !editor.has_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with"):
+ editor.set_setting("plugin/gd_override_functions/inheritance/virtual_functions_begins_with", _char_virtual_function)
+ if !editor.has_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with"):
+ editor.set_setting("plugin/gd_override_functions/inheritance/private_functions_begins_with", _char_private_function)
+
+ if !editor.has_setting("plugin/gd_override_functions/interface/class_as_interface_if_begins_with"):
+ editor.set_setting("plugin/gd_override_functions/interface/class_as_interface_if_begins_with", _interface_begins_with)
+ if !editor.has_setting("plugin/gd_override_functions/interface/class_as_interface_if_end_with"):
+ editor.set_setting("plugin/gd_override_functions/interface/class_as_interface_if_end_with", _interface_end_with)
+ if !editor.has_setting("plugin/gd_override_functions/interface/class_interface_name_ignore_case"):
+ editor.set_setting("plugin/gd_override_functions/interface/class_interface_name_ignore_case", false)
+ if !editor.has_setting("plugin/gd_override_functions/inheritance/include_native_class_for_check_interfaces"):
+ editor.set_setting("plugin/gd_override_functions/inheritance/include_native_class_for_check_interfaces", _include_native_class_for_check_interfaces)
+
+ editor.settings_changed.connect(_on_settings_change)
+
+ _update_gui()
+#endregion
+
+func _on_change_visibility() -> void:
+ if !visible:
+ _created_funcs.clear()
+ _buffer_data.clear()
+
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ editor.set_setting("plugin/gd_override_functions/generate_at_end_line", _generate_at_end_line)
+ editor.set_setting("plugin/gd_override_functions/order_inverted", _last_filter == FILTER_TYPE.REVERSE)
+
+ return
+
+func _update_gui() -> void:
+ if accept_button:
+ accept_button.disabled = tree.get_selected() == null
+
+ if public_button:
+ public_button.button_pressed = _public_filter
+
+ if protected_button:
+ protected_button.button_pressed = _protected_filter
+
+ if private_button:
+ private_button.button_pressed = _private_filter
+
+ if interface_button:
+ interface_button.button_pressed = _interface_filter
+
+ if order_button:
+ if _last_filter == FILTER_TYPE.DEFAULT or _last_filter == FILTER_TYPE.DEFAUL_TREE:
+ order_button.icon = ICON_ORDER_INVERT
+ else:
+ order_button.icon = ICON_ORDER_DEFAULT
+
+ if check_generate_at_line:
+ check_generate_at_line.button_pressed = _generate_at_end_line
+
+ #UPDATE INTERFACE
+ if virtual_generate_button:
+ virtual_generate_button.disabled = true
+ if _buffer_data.size() > 0:
+ for x : Variant in _buffer_data.keys():
+ if _buffer_data[x]["type"] > 0:
+ var _class_data : Dictionary = _buffer_data[x]
+ var _funcs : Dictionary = _class_data["funcs"]
+ for _func : Variant in _funcs.keys():
+ var func_name : String = str(_func)
+ if !func_name.begins_with(_char_private_function) and func_name.begins_with(_char_virtual_function):
+ if !_created_funcs.has(_func):
+ virtual_generate_button.disabled = false
+ break
+ if !virtual_generate_button.disabled:
+ break
+
+ #UPDATE INTERFACE
+ if interface_generate_button:
+ interface_generate_button.disabled = true
+ if _buffer_data.size() == 0:
+ return
+ for x : Variant in _buffer_data.keys():
+ if _buffer_data[x]["type"] > 1:
+ var _class_data : Dictionary = _buffer_data[x]
+ var _funcs : Dictionary = _class_data["funcs"]
+ for _func : Variant in _funcs.keys():
+ if !_created_funcs.has(_func):
+ interface_generate_button.disabled = false
+ return
+
+func _write_lines(_class_name : String, func_name : String, input_script : Script, data : String, is_interface : bool = false) -> bool:
+ #ONLY EDITOR MODE
+ if !Engine.is_editor_hint():
+ print(data)
+ return false
+
+ var comment : String = "Override {0} {1}."
+ var type : String = "function"
+
+ if is_interface:
+ comment = "Implement {0} {1}."
+
+ if func_name.begins_with(_char_private_function):
+ type = "private function"
+ elif func_name.begins_with(_char_virtual_function):
+ type = "virtual function"
+
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var scripts : Array[Script] = script_editor.get_open_scripts()
+ var scripts_editor : Array[ScriptEditorBase] = script_editor.get_open_script_editors()
+ var edit : CodeEdit = null
+ var iscript : int = -1
+
+ for x : int in range(scripts.size()):
+ if scripts[x] == input_script:
+ iscript = x
+ break
+
+ if iscript == -1 or iscript >= scripts_editor.size():
+ push_error("Error, can`t get editor!")
+ return false
+
+ edit = scripts_editor[iscript].get_base_editor()
+
+ var new_line : String = str("#", comment.format([_class_name,type]),"\n", data)
+ if !_generate_at_end_line and edit.get_caret_count() > 0:
+ var line : int = -1
+ for x : int in edit.get_caret_count():
+ line = edit.get_caret_line(x)
+ break
+ if line > -1:
+ var line_to : int = -1
+ while line < edit.get_line_count():
+ var ctxline : String = edit.get_line(line)
+ if ctxline.length() > 0:
+ if ctxline.begins_with(" ") or ctxline.begins_with("\t"):
+ if !ctxline.strip_edges().is_empty():
+ line_to = -1
+ else:
+ line_to = line
+ else:
+ if line_to == -1:
+ line_to = line
+ break
+ else:
+ if line_to == -1:
+ line_to = line
+ line += 1
+ if line_to > -1 and line_to != edit.get_line_count() - 1:
+ if line_to > 1:
+ if !(edit.get_line(line_to - 1).strip_edges().is_empty()):
+ new_line = str('\n', new_line)
+ if line_to < edit.get_line_count() - 1:
+ if !(edit.get_line(line_to + 1).strip_edges().is_empty()):
+ new_line = str(new_line, '\n')
+ var ctx : String = edit.get_line(line_to)
+ if !ctx.strip_edges().is_empty():
+ edit.set_line(line_to, new_line+'\n'+edit.get_line(line_to))
+ else:
+ edit.set_line(line_to, new_line)
+ _goto_line(script_editor, line_to)
+ return true
+
+ if edit.text.ends_with("\n"):
+ edit.text += str("\n", new_line)
+ else:
+ edit.text += str("\n\n", new_line)
+ _goto_line(script_editor, edit.get_line_count() - 1)
+ return true
+
+# goto_line script-ide
+func _goto_line(script_editor : ScriptEditor, index : int):
+ script_editor.goto_line(index)
+
+ var code_edit: CodeEdit = script_editor.get_current_editor().get_base_editor()
+ code_edit.set_caret_line(index)
+ code_edit.set_v_scroll(index)
+ code_edit.set_caret_column(code_edit.get_line(index).length())
+ code_edit.set_h_scroll(0)
+
+ code_edit.grab_focus()
+
+func __iterate_metada(buffer : PackedStringArray, input_script : Script, funcs : Dictionary, metadata : Array[Dictionary], totals : int = 0) -> int:
+ if totals < funcs.size():
+ for key : Variant in funcs.keys():
+ var data : Dictionary = funcs[key]
+ var class_type : int = data["type"]
+ var _class_name : String = data["class"]
+ var _func : String = data["name"]
+
+ var is_interface : bool = class_type == 2
+
+ for meta : Dictionary in metadata:
+ if meta.name == _func:
+ if _func in buffer:
+ continue
+ buffer.append(_func)
+ if _write_lines(_class_name, _func, input_script, _get_full_header_virtual(meta), is_interface):
+ if is_interface:
+ print('[INFO] Created "{0}.{1}" interface function'.format([_class_name, _func]))
+ else:
+ print('[INFO] Created "{0}.{1}" function'.format([_class_name, _func]))
+ else:
+ if Engine.is_editor_hint():
+ if is_interface:
+ print('[INFO] Error on create "{0}.{1}" interface function!'.format([_class_name, _func]))
+ else:
+ print('[INFO] Error on create "{0}.{1}" function!'.format([_class_name, _func]))
+ totals += 1
+ if totals == funcs.size():
+ break
+ return totals
+
+#region UI_CALLBACK
+func _on_accept_button() -> void:
+ var item : TreeItem = tree.get_next_selected(null)
+ var funcs : Dictionary = {}
+
+ while item != null:
+ var parent : String = item.get_parent().get_text(0)
+ var fname : String = item.get_text(0)
+
+ for x : Variant in _buffer_data.keys():
+ if _buffer_data[x]["name"] == parent:
+ var _class_data : Dictionary = _buffer_data[x]
+ var _funcs : Dictionary = _class_data["funcs"]
+ if _funcs.has(fname):
+ funcs[fname] = {
+ "type" : _class_data["type"]
+ ,"class" :_class_data["name"]
+ ,"name" : fname
+ }
+ item = tree.get_next_selected(item)
+
+ _make(funcs)
+
+func _make(funcs : Dictionary) -> void:
+ var type_base : StringName = _last_script.get_instance_base_type()
+ var buffer : PackedStringArray = []
+ if ClassDB.class_exists(type_base):
+ __iterate_metada(buffer, _last_script, funcs, ClassDB.class_get_method_list(type_base), __iterate_metada(buffer, _last_script, funcs, _last_script.get_script_method_list(), 0),)
+ else:
+ __iterate_metada(buffer, _last_script, funcs, _last_script.get_script_method_list(), 0)
+ _on_cancel_button()
+
+
+
+func _on_cancel_button() -> void:
+ hide()
+ config_update(true)
+
+func _on_tree_multi_selected(_item: TreeItem, _column: int, _selected: bool) -> void:
+ _update_gui()
+#endregion
+
+func _get_name(script : Script, ref_data : Dictionary) -> StringName:
+ var base_name : StringName = script.get_global_name()
+ if base_name.is_empty():
+ var path : String = script.resource_name
+ if path.is_empty():
+ path = script.resource_path
+ if !path.is_empty():
+ var _name : String = path.get_file()
+ _name = _name.trim_suffix("." + _name.get_extension())
+ base_name = _name
+ else:
+ base_name = &"CustomScript"
+ ref_data["custom"] = true
+ else:
+ base_name = path
+ return base_name
+
+func _clear_funcs(script : Script) -> Dictionary:
+ var out : Dictionary = {}
+ if Engine.is_editor_hint():
+ var rgx : RegEx = RegEx.create_from_string("(?m)^func\\s+(\\w*)\\s*\\(")
+ var source : String = script.source_code
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var scripts_editors : Array[ScriptEditorBase] = script_editor.get_open_script_editors()
+ var scripts : Array[Script] = script_editor.get_open_scripts()
+ var iscript : int = -1
+
+ for x : int in range(scripts.size()):
+ if scripts[x] == script:
+ iscript = x
+ break
+ if iscript > -1 and scripts_editors.size() > iscript:
+ source = scripts_editors[iscript].get_base_editor().text
+ for rs : RegExMatch in rgx.search_all(source):
+ if rs.strings.size() > 1:
+ var fname : String = rs.strings[1]
+ out[fname] = fname
+ else:
+ for methods : Dictionary in script.get_script_method_list():
+ out[methods.name] = methods.name
+ return out
+
+func _generate_native(native : StringName, data : Dictionary, index : int = 0) -> int:
+ if native.is_empty() or !ClassDB.class_exists(native):
+ return index
+ var funcs : Dictionary = {}
+ var base : Dictionary = {
+ "name" : native
+ ,"funcs" : funcs
+ ,"type" : 0
+ ,"custom": false
+ }
+ index += 1
+ data[index] = base
+
+ if _include_native_class_for_check_interfaces:
+ var base_name : String = native
+ if _interface_ignore_case:
+ base_name = base_name.to_lower()
+ if (!_interface_begins_with.is_empty() and base_name.begins_with(_interface_begins_with)) or \
+ (!_interface_end_with.is_empty() and base_name.ends_with(_interface_end_with)):
+ base["type"] = 3
+
+ if _interface_filter and base["type"] == 3:
+ #SHOW ALL
+ for dict: Dictionary in ClassDB.class_get_method_list(native):
+ funcs[dict.name] = _get_header_virtual(dict)
+ else:
+ for dict: Dictionary in ClassDB.class_get_method_list(native):
+ #region conditional
+ if _protected_filter:
+ if dict.flags & METHOD_FLAG_VIRTUAL > 0:
+ funcs[dict.name] =_get_header_virtual(dict)
+ continue
+ if _public_filter:
+ var method : StringName = dict.name
+ if _private_begin_equal_protected:
+ if !method.begins_with(_char_virtual_function):
+ funcs[method] = _get_header_virtual(dict)
+ continue
+ else:
+ if !method.begins_with(_char_private_function) and !method.begins_with(_char_virtual_function):
+ funcs[method] =_get_header_virtual(dict)
+ continue
+ if _private_filter:
+ var method : StringName = dict.name
+ if _private_begin_equal_protected:
+ if method.begins_with(_char_private_function):
+ funcs[method] =_get_header_virtual(dict)
+ else:
+ if method.begins_with(_char_private_function) and !method.begins_with(_char_virtual_function):
+ funcs[method] =_get_header_virtual(dict)
+ #endregion
+
+ for x : int in range(0, index, 1):
+ var clazz : Dictionary = data[x]["funcs"]
+ for k : Variant in funcs.keys():
+ if clazz.has(k):
+ clazz.erase(k)
+
+ return _generate_native(ClassDB.get_parent_class(native), data, index)
+
+func _generate(script : Script, data : Dictionary, index : int = -1) -> int:
+ if script == null:
+ return index
+ var funcs : Dictionary = {}
+ var base : Dictionary = {
+ "name" : &"GDScript"
+ ,"funcs" : funcs
+ ,"type": 1
+ ,"custom": false
+ }
+ var base_name : String = _get_name(script, base)
+ base["name"] = base_name
+ index += 1
+ data[index] = base
+
+ if _interface_ignore_case:
+ base_name = base_name.to_lower()
+
+ if (!_interface_begins_with.is_empty() and base_name.begins_with(_interface_begins_with)) or \
+ (!_interface_end_with.is_empty() and base_name.ends_with(_interface_end_with)):
+ base["type"] = 2
+
+ if _interface_filter and base["type"] == 2:
+ #SHOW ALL
+ for dict: Dictionary in script.get_script_method_list():
+ if dict.name.begins_with("@"):continue
+ funcs[dict.name] = _get_header_virtual(dict)
+ else:
+ for dict: Dictionary in script.get_script_method_list():
+ var func_name: StringName = dict.name
+ if func_name.begins_with("@"):continue
+ #region conditional
+ if _protected_filter:
+ if (func_name.begins_with(_char_virtual_function) and !func_name.begins_with(_char_private_function)) or dict.flags & METHOD_FLAG_VIRTUAL > 0:
+ funcs[func_name] = _get_header_virtual(dict)
+ if _public_filter:
+ if _private_begin_equal_protected:
+ if !func_name.begins_with(_char_virtual_function):
+ funcs[func_name] =_get_header_virtual(dict)
+ continue
+ else:
+ if !func_name.begins_with(_char_private_function) and !func_name.begins_with(_char_virtual_function):
+ funcs[func_name] =_get_header_virtual(dict)
+ continue
+ if _private_filter:
+ if _private_begin_equal_protected:
+ if func_name.begins_with(_char_private_function):
+ funcs[func_name] =_get_header_virtual(dict)
+ else:
+ if func_name.begins_with(_char_private_function) and !func_name.begins_with(_char_virtual_function):
+ funcs[func_name] =_get_header_virtual(dict)
+ #endregion
+
+ for x : int in range(0, index, 1):
+ var clazz : Dictionary = data[x]["funcs"]
+ for k : Variant in funcs.keys():
+ if clazz.has(k):
+ clazz.erase(k)
+ return _generate(script.get_base_script(), data, index)
+
+func __is_variant(func_name : String) -> bool:
+ const FUNC_GET : Array[String] = ["get_", "_get"]
+ for x : String in FUNC_GET:
+ if func_name.begins_with(x) or func_name.ends_with(x):
+ return true
+ return func_name.contains("_get_")
+
+func _get_header_virtual(dict : Dictionary) -> String:
+ var params : String = ""
+ var args : Array = dict["args"]
+ var separator : String = ""
+ var default_args : Array = dict["default_args"]
+ var _default_index : int = default_args.size()
+
+ for y : int in range(args.size() - 1, -1, -1):
+ var arg : Dictionary = args[y]
+ var txt : String = "" #arg["name"]
+ if !(arg["class_name"]).is_empty():
+ txt += str(arg["class_name"] as String)
+ else:
+ var _typeof : int = arg["type"]
+ txt += str(_get_type(_typeof))
+ if include_paremeters and _default_index > 0:
+ _default_index -= 1
+ var def : Variant = default_args[_default_index]
+ var _type : int = typeof(def)
+ if def == null or _type < 1:
+ txt += str(' = null')
+ elif _type < 5:
+ if def is String:
+ txt += str(' = "', def, '"')
+ elif def is StringName:
+ txt += str(' = &"', def, '"')
+ else:
+ txt += str(" = ", def)
+ else:
+ txt += str(" = ",_get_type(typeof(def)), def)
+ params = str(txt, separator, params)
+ separator = ", "
+
+ var return_dic : Dictionary = dict["return"]
+ var return_type : String = "void"
+
+ if !return_dic["class_name"].is_empty():
+ return_type = (return_dic["class_name"] as String)
+ else:
+ var _type : int = return_dic["type"]
+ if _type < 1:
+ var func_name : String = str(dict["name"]).to_lower()
+ if func_name == "get" or __is_variant(func_name):
+ return_type = "Variant"
+ else:
+ return_type = "void"
+ else:
+ return_type = _get_type(return_dic["type"])
+
+ if params.is_empty():
+ params = "-"
+ return "{0}||{1}||{2}".format([dict["name"], params, return_type]).replace(" ", "") #Replace x more space.
+
+func _get_full_header_virtual(dict : Dictionary) -> String:
+ var params : String = ""
+ var args : Array = dict["args"]
+ var separator : String = ""
+ var default_args : Array = dict["default_args"]
+ var _default_index : int = default_args.size()
+
+ for y : int in range(args.size() - 1, -1, -1):
+ var arg : Dictionary = args[y]
+ var txt : String = arg["name"]
+ if !(arg["class_name"]).is_empty():
+ txt += str(" : ", arg["class_name"] as String)
+ else:
+ var _typeof : int = arg["type"]
+ txt += str(" : ", _get_type(_typeof))
+ if _default_index > 0:
+ _default_index -= 1
+ var def : Variant = default_args[_default_index]
+ var _type : int = typeof(def)
+ if def == null or _type < 1:
+ txt += str(' = null')
+ elif _type < 5:
+ if def is String:
+ txt += str(' = "', def, '"')
+ elif def is StringName:
+ txt += str(' = &"', def, '"')
+ else:
+ txt += str(" = ", def)
+ else:
+ txt += str(" = ",_get_type(typeof(def)), def)
+ params = str(txt, separator, params)
+ separator = ", "
+
+ var return_dic : Dictionary = dict["return"]
+ var return_type : String = "void"
+ var return_value : String = "pass"
+ if !return_dic["class_name"].is_empty():
+ return_type = (return_dic["class_name"] as String)
+ return_value = "return null"
+ else:
+ var _type : int = return_dic["type"]
+ if _type < 1:
+ var func_name : String = str(dict["name"]).to_lower()
+ if func_name == "get" or __is_variant(func_name):
+ return_type = "Variant"
+ return_value = "return null"
+ else:
+ return_type = "void"
+ else:
+ return_type = _get_type(return_dic["type"])
+ if _type == TYPE_INT:
+ return_value = "return 0"
+ elif _type == TYPE_BOOL:
+ return_value = "return false"
+ elif _type == TYPE_FLOAT:
+ return_value = "return 0.0"
+ elif _type == TYPE_STRING:
+ return_value = 'return ""'
+ elif _type == TYPE_ARRAY:
+ return_value = "return []"
+ else:
+ return_value = str("return ", return_type,"()")
+ return "func {0}({1}) -> {2}:\n\t#TODO: code here :)\n\t{3}".format([dict["name"], params, return_type, return_value])
+
+func _get_type(_typeof : int) -> String:
+ var txt : String = ""
+ match _typeof:
+ TYPE_BOOL : txt = "bool"
+ TYPE_INT : txt = "int"
+ TYPE_FLOAT: txt = "float"
+ TYPE_STRING : txt = "String"
+ TYPE_VECTOR2 : txt = "Vector2"
+ TYPE_VECTOR2I : txt = "Vector2i"
+ TYPE_RECT2 : txt = "Rect2"
+ TYPE_RECT2I : txt = "Rect2i"
+ TYPE_VECTOR3 : txt = "Vector3"
+ TYPE_VECTOR3I : txt = "Vector3i"
+ TYPE_TRANSFORM2D : txt = "Tranform2D"
+ TYPE_VECTOR4 : txt = "Vector4"
+ TYPE_VECTOR4I : txt = "Vector4i"
+ TYPE_PLANE : txt = "Plane"
+ TYPE_QUATERNION : txt = "Quaternion"
+ TYPE_AABB : txt = "AABB"
+ TYPE_BASIS : txt = "Basis"
+ TYPE_TRANSFORM3D : txt = "Transform3D"
+ TYPE_PROJECTION : txt = "Projection"
+ TYPE_COLOR : txt = "Color"
+ TYPE_STRING_NAME : txt = "StringName"
+ TYPE_NODE_PATH : txt = "NodePath"
+ TYPE_RID : txt = "RID"
+ TYPE_OBJECT : txt = "Object"
+ TYPE_CALLABLE : txt = "Callable"
+ TYPE_SIGNAL : txt = "Signal"
+ TYPE_DICTIONARY : txt = "Dictionary"
+ TYPE_ARRAY : txt = "Array"
+ TYPE_PACKED_BYTE_ARRAY : txt = "PackedByteArray"
+ TYPE_PACKED_INT32_ARRAY : txt = "PackedInt32Array"
+ TYPE_PACKED_INT64_ARRAY : txt = "PackedInt64Array"
+ TYPE_PACKED_FLOAT32_ARRAY : txt = "PackedFloat32Array"
+ TYPE_PACKED_FLOAT64_ARRAY : txt = "PackedFloat64Array"
+ TYPE_PACKED_STRING_ARRAY : txt = "PackedStringArray"
+ TYPE_PACKED_VECTOR2_ARRAY : txt = "PackedVector2Array"
+ TYPE_PACKED_VECTOR3_ARRAY : txt = "PackedVector3Array"
+ TYPE_PACKED_COLOR_ARRAY : txt = "PackedColorArray"
+ TYPE_PACKED_VECTOR4_ARRAY : txt = "PackedVector4Array"
+ _:
+ txt = "Variant"
+ return txt
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd.uid b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd.uid
new file mode 100644
index 0000000..530ff15
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd.uid
@@ -0,0 +1 @@
+uid://bnbi70ajc0nbg
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.tscn b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.tscn
new file mode 100644
index 0000000..e05b3c6
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.tscn
@@ -0,0 +1,196 @@
+[gd_scene load_steps=10 format=3 uid="uid://hxjjupcjoat8"]
+
+[ext_resource type="Script" uid="uid://bnbi70ajc0nbg" path="res://addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.gd" id="1_afp4m"]
+[ext_resource type="Texture2D" uid="uid://bgjo43cuidob1" path="res://addons/_Godot-IDE_/shared_resources/up.svg" id="2_v42ap"]
+[ext_resource type="Texture2D" uid="uid://dg7rdmg80x4jv" path="res://addons/_Godot-IDE_/shared_resources/func_virtual.svg" id="3_t5ujg"]
+[ext_resource type="Texture2D" uid="uid://bjmtfc58y1sbs" path="res://addons/_Godot-IDE_/shared_resources/InterfaceScript.svg" id="4_yees5"]
+[ext_resource type="StyleBox" uid="uid://daq1bri8pp6lp" path="res://addons/_Godot-IDE_/plugins/gd_override_functions/popup/button/pressed.tres" id="5_obeej"]
+[ext_resource type="Texture2D" uid="uid://ckc3yk6f8y3ob" path="res://addons/_Godot-IDE_/shared_resources/func_public.svg" id="6_kuyjr"]
+[ext_resource type="Texture2D" uid="uid://do4gmovks0mn6" path="res://addons/_Godot-IDE_/shared_resources/func_private.svg" id="7_edrjb"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_iq5ej"]
+bg_color = Color(0.352765, 0.124129, 0.353458, 1)
+border_width_left = 4
+border_width_top = 4
+border_width_right = 4
+border_color = Color(0.631373, 0.270588, 0.631373, 1)
+corner_radius_top_left = 2
+corner_radius_top_right = 2
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_knkse"]
+bg_color = Color(0.196078, 0.392157, 0.588235, 1)
+
+[node name="Virtuals" type="PopupPanel" node_paths=PackedStringArray("tree", "accept_button", "cancel_button", "check_generate_at_line", "interface_generate_button", "virtual_generate_button", "order_button", "public_button", "protected_button", "private_button", "interface_button")]
+title = "GD Override Functions"
+initial_position = 4
+size = Vector2i(1024, 512)
+visible = true
+transient_to_focused = true
+unresizable = false
+borderless = false
+script = ExtResource("1_afp4m")
+tree = NodePath("Container/Tree")
+accept_button = NodePath("Container/FooterContainer/OkButton")
+cancel_button = NodePath("Container/FooterContainer/CancelButton")
+check_generate_at_line = NodePath("Container/CheckConfig0")
+interface_generate_button = NodePath("Container/HBoxContainer/interface_generate_btn")
+virtual_generate_button = NodePath("Container/HBoxContainer/virtual_generate_btn")
+order_button = NodePath("Container/HBoxContainer/order")
+public_button = NodePath("Container/HBoxContainer/public_btn")
+protected_button = NodePath("Container/HBoxContainer/protected_btn")
+private_button = NodePath("Container/HBoxContainer/private_btn")
+interface_button = NodePath("Container/HBoxContainer/interface_btn")
+
+[node name="Container" type="VBoxContainer" parent="."]
+offset_left = 4.0
+offset_top = 4.0
+offset_right = 1020.0
+offset_bottom = 508.0
+
+[node name="TittleContainer" type="PanelContainer" parent="Container"]
+custom_minimum_size = Vector2(24, 24)
+layout_mode = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_iq5ej")
+
+[node name="Tittle" type="Label" parent="Container/TittleContainer"]
+layout_mode = 2
+size_flags_horizontal = 6
+text = "Select Methods to Override/Implement"
+horizontal_alignment = 1
+vertical_alignment = 1
+
+[node name="TextureRect" type="TextureRect" parent="Container/TittleContainer"]
+custom_minimum_size = Vector2(16, 16)
+layout_mode = 2
+size_flags_horizontal = 8
+expand_mode = 1
+stretch_mode = 3
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Container"]
+layout_mode = 2
+
+[node name="order" type="Button" parent="Container/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Order by class inherited."
+text = "Order"
+icon = ExtResource("2_v42ap")
+
+[node name="virtual_generate_btn" type="Button" parent="Container/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Implement all custom virtual methods.
+(Only custom virtuals methods!)"
+disabled = true
+text = "Generate"
+icon = ExtResource("3_t5ujg")
+
+[node name="interface_generate_btn" type="Button" parent="Container/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Implement all interface methods."
+disabled = true
+text = "Generate"
+icon = ExtResource("4_yees5")
+
+[node name="Label" type="Label" parent="Container/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 10
+text = "Filters Methods"
+
+[node name="public_btn" type="Button" parent="Container/HBoxContainer"]
+custom_minimum_size = Vector2(16, 16)
+layout_mode = 2
+tooltip_text = "Public methods.
+
+Example:
+func public_method()
+func my_method()
+func foo()"
+theme_override_font_sizes/font_size = 16
+theme_override_styles/pressed = ExtResource("5_obeej")
+toggle_mode = true
+icon = ExtResource("6_kuyjr")
+icon_alignment = 1
+
+[node name="protected_btn" type="Button" parent="Container/HBoxContainer"]
+custom_minimum_size = Vector2(16, 16)
+layout_mode = 2
+tooltip_text = "Protected/Virtual Methods.
+
+Example:
+func _virtual_method()
+func _my_virtual_method()
+func _ready()"
+theme_override_styles/pressed = ExtResource("5_obeej")
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("3_t5ujg")
+icon_alignment = 1
+
+[node name="private_btn" type="Button" parent="Container/HBoxContainer"]
+custom_minimum_size = Vector2(16, 16)
+layout_mode = 2
+tooltip_text = "Private Methods.
+
+Example:
+func __private_method()
+func __my_private_method
+func __foo()"
+theme_override_styles/pressed = ExtResource("5_obeej")
+toggle_mode = true
+icon = ExtResource("7_edrjb")
+icon_alignment = 1
+
+[node name="interface_btn" type="Button" parent="Container/HBoxContainer"]
+custom_minimum_size = Vector2(16, 16)
+layout_mode = 2
+tooltip_text = "Interfaces.
+This include all interface methods.
+
+Interfaces are the \"custom classes\" begin with \"I\" character.
+
+Example:
+class_name IMyClass
+class_name IDummy_Class
+class_name Ifoo_class
+
+Files without class_name but begin with \"I\" also work!
+
+Example:
+IMyInterface.gd
+IDummy_Script.gd
+Ifoo_script.gd"
+theme_override_styles/pressed = ExtResource("5_obeej")
+toggle_mode = true
+button_pressed = true
+icon = ExtResource("4_yees5")
+icon_alignment = 1
+
+[node name="Tree" type="Tree" parent="Container"]
+custom_minimum_size = Vector2(128, 256)
+layout_mode = 2
+size_flags_vertical = 3
+columns = 3
+column_titles_visible = true
+hide_root = true
+select_mode = 2
+
+[node name="CheckConfig0" type="CheckBox" parent="Container"]
+layout_mode = 2
+button_pressed = true
+text = "Generate at end of line"
+
+[node name="FooterContainer" type="HBoxContainer" parent="Container"]
+layout_mode = 2
+
+[node name="OkButton" type="Button" parent="Container/FooterContainer"]
+custom_minimum_size = Vector2(64, 0)
+layout_mode = 2
+size_flags_horizontal = 3
+theme_override_styles/normal = SubResource("StyleBoxFlat_knkse")
+disabled = true
+text = "Create"
+
+[node name="CancelButton" type="Button" parent="Container/FooterContainer"]
+custom_minimum_size = Vector2(64, 0)
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Cancel"
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd
new file mode 100644
index 0000000..2d99f66
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd
@@ -0,0 +1,52 @@
+@tool
+extends EditorContextMenuPlugin
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# script-ide: Virtual Popups
+#
+# Virtual Popups for script-ide addon.godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const SCENE : PackedScene = preload("res://addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup.tscn")
+const ICON : Texture = preload("res://addons/_Godot-IDE_/shared_resources/func_virtual.svg")
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ var root : Node = Engine.get_main_loop().root
+ var virtual_popup : Popup = root.get_node_or_null("_VPOPUP_")
+ if is_instance_valid(virtual_popup) and !virtual_popup.is_queued_for_deletion():
+ virtual_popup.config_update(true)
+ virtual_popup.queue_free()
+
+func callback(input : Object) -> void:
+ var input_script : Script = null
+
+ if input is Script:
+ input_script = input
+ elif input is CodeEdit:
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var scripts_editors : Array[ScriptEditorBase] = script_editor.get_open_script_editors()
+ var scripts : Array[Script] = script_editor.get_open_scripts()
+ var iscript : int = -1
+
+ for x : int in range(scripts_editors.size()):
+ if scripts_editors[x].get_base_editor() == input:
+ iscript = x
+ pass
+ if iscript > -1 and iscript < scripts.size():
+ input_script = scripts[iscript]
+
+ if null == input_script:
+ push_error("[PLUGIN] Error, can`t get current script - not valid!")
+ return
+
+ var root : Node = Engine.get_main_loop().root
+ var virtual_popup : Popup = root.get_node_or_null("_VPOPUP_")
+ if virtual_popup == null:
+ virtual_popup = SCENE.instantiate()
+ virtual_popup.set(&"name", &"_VPOPUP_")
+ root.add_child(virtual_popup)
+ virtual_popup.make_tree(input_script)
+ virtual_popup.popup_centered.call_deferred()
+
+func _popup_menu(_paths : PackedStringArray) -> void:
+ add_context_menu_item("Override Virtual Functions", callback, ICON)
diff --git a/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd.uid b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd.uid
new file mode 100644
index 0000000..219eddf
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/gd_override_functions/popup/virtuals_popup_context.gd.uid
@@ -0,0 +1 @@
+uid://bcfsicur2au8j
diff --git a/addons/_Godot-IDE_/plugins/macro-n/context.gd b/addons/_Godot-IDE_/plugins/macro-n/context.gd
new file mode 100644
index 0000000..1a989ef
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/context.gd
@@ -0,0 +1,65 @@
+extends EditorContextMenuPlugin
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+const INTERFACE_SCRIPT = preload("res://addons/_Godot-IDE_/shared_resources/InterfaceScript.svg")
+var _helper : Object = null
+var _fragments : Window = null
+
+var controller : Object = null
+
+
+func _init(helper : Object) -> void:
+ _helper = helper
+
+#Override EditorContextMenuPlugin virtual function.
+func _popup_menu(_paths : PackedStringArray) -> void:
+ if controller == null:
+ controller = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd").new()
+ var txt : String = controller.execute()
+ if txt.is_empty():
+ return
+ add_context_menu_item("Macro-N", _on_save.bind(txt), INTERFACE_SCRIPT)
+
+func _on_save(_variant : Variant, text : String) -> void:
+ _helper.call(&"half_life", text, -1)
+
+func show_macros() -> void:
+ if !is_instance_valid(_fragments):
+ var fragment : PackedScene = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/Fragments.tscn")
+ _fragments = fragment.instantiate()
+ _helper.add_child(_fragments)
+ var db : Object = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd").new()
+ _fragments.set_dependencies(
+ [
+ ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd").new(db),
+ ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd").new(db),
+ ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd").new(db)
+ ]
+ )
+
+ _fragments.on_create.connect(_helper.create_new)
+
+ _fragments.popup_centered()
+
+
+func invoke_macron_bypass() -> void:
+ if controller == null:
+ controller = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd").new()
+ var txt : String = controller.execute()
+ txt = txt.strip_edges()
+ if txt.is_empty():
+ return
+ _helper.call(&"half_life", txt, 1)
+
+func invoke_macron() -> void:
+ if controller == null:
+ controller = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd").new()
+ var txt : String = controller.execute()
+ txt = txt.strip_edges()
+ if txt.is_empty():
+ return
+ _helper.call(&"half_life", txt, 0)
diff --git a/addons/_Godot-IDE_/plugins/macro-n/context.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/context.gd.uid
new file mode 100644
index 0000000..0d63db0
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/context.gd.uid
@@ -0,0 +1 @@
+uid://cstqqmdlwkth
diff --git a/addons/_Godot-IDE_/plugins/macro-n/macro-n.gd b/addons/_Godot-IDE_/plugins/macro-n/macro-n.gd
new file mode 100644
index 0000000..dd4b90f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/macro-n.gd
@@ -0,0 +1,112 @@
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+var user_path : String = ""
+var templates_path : String = ""
+
+var _pop_warn : ConfirmationDialog
+
+var _helper : Node = null
+
+func _init(helper : Node) -> void:
+ assert(helper != null)
+ _helper = helper
+
+func _get_pop() -> Object:
+ var packed : PackedScene = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/context/Macro-N.tscn")
+ return packed.instantiate()
+
+func feed() -> void:
+ var edit : ScriptEditor = EditorInterface.get_script_editor()
+
+ if edit:
+ var base : ScriptEditorBase = edit.get_current_editor()
+ if base:
+ var be : Control = base.get_base_editor()
+ if be is CodeEdit:
+ pipe_pop_in(be.text)
+
+ #NO SYTNAX
+ return
+
+func pipe_pop_in(txt : String) -> void:
+ txt = parse_text(txt)
+ if txt.is_empty():
+ print("[Macro-N] Error!, empty syntax!")
+ return
+
+ var pop : Node = _get_pop()
+ if !pop.is_inside_tree():
+ _helper.add_child(pop)
+ pop.callback = pipe_save
+ pop.call(&"show_feed", txt)
+
+func pipe_save(path : String, txt : String) -> void:
+ if !DirAccess.dir_exists_absolute(path):
+ print("[Macro-N]: Can not find dir save path!")
+ return
+
+ path = path.strip_edges()
+ if path.is_empty():
+ path = "My Macro File"
+
+ txt = parse_text(txt)
+
+ var end : String = user_path.path_join(path + ".mn")
+ if !FileAccess.file_exists(end):
+ if !_save(end, txt):
+ print("[Macro-N] Can not save syntax! ", end)
+ else:
+ print("[Macron-N] Saved syntax: ", end)
+ return
+ _save_warn(end, txt)
+
+func _save(end : String, txt : String) -> bool:
+ var file : FileAccess = FileAccess.open(end, FileAccess.WRITE)
+ if !file:
+ return false
+ return file.store_string(txt)
+
+func _save_warn(end : String, txt : String) -> void:
+ if !is_instance_valid(_pop_warn):
+ _pop_warn = ConfirmationDialog.new()
+ _pop_warn.title = "Already Exist File, Ovewrite?"
+ _pop_warn.canceled.connect(func():_pop_warn.queue_free())
+ _pop_warn.confirmed.connect(
+ func():
+ _save(end, txt)
+ _pop_warn.queue_free()
+ )
+ _helper.add_child(_pop_warn)
+ _pop_warn.popup_centered()
+
+func parse_text(txt : String) -> String:
+ var split : PackedStringArray = txt.split("\n", true, 0)
+ var maxt : Array[int] = [0, 0]
+
+ while split.size() > 0 and split[0].strip_edges().length() == 0:
+ split.remove_at(0)
+
+ if split.size() == 0:
+ return ""
+
+ for xline : int in range(split.size()):
+ var line : String = split[xline]
+ var indx : int = 0
+ var cline : int = mini(xline, 1)
+ while line.length() > indx and (line[indx] == '\t' or line[indx] == ' '):
+ indx += 1
+ maxt[cline] = maxi(maxt[cline], indx)
+
+ if maxt[0] < maxt[1]:
+ var st : int = maxt[0]
+ for xline : int in range(split.size()):
+ split[xline] = split[xline].substr(st, -1)
+
+ return "\n".join(split)
diff --git a/addons/_Godot-IDE_/plugins/macro-n/macro-n.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/macro-n.gd.uid
new file mode 100644
index 0000000..86a18bb
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/macro-n.gd.uid
@@ -0,0 +1 @@
+uid://burhkdjv1q8bu
diff --git a/addons/_Godot-IDE_/plugins/macro-n/plugin.cfg b/addons/_Godot-IDE_/plugins/macro-n/plugin.cfg
new file mode 100644
index 0000000..ad31b74
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="Macro-N"
+description="Allow generate code by shortcut"
+author="Twister"
+version="Alpha-1.0"
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/macro-n/plugin.gd b/addons/_Godot-IDE_/plugins/macro-n/plugin.gd
new file mode 100644
index 0000000..bd0c34d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/plugin.gd
@@ -0,0 +1,77 @@
+@tool
+extends EditorPlugin
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const CONTEXT := preload("res://addons/_Godot-IDE_/plugins/macro-n/context.gd")
+
+var ctx_macron_n : EditorContextMenuPlugin = null
+var macron_n : RefCounted = null
+
+var _c_input : InputEvent = null
+var _g_input : InputEvent = null
+var _cb_input : InputEvent = null
+
+
+func _enter_tree() -> void:
+ ctx_macron_n = CONTEXT.new(self)
+ add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, ctx_macron_n)
+
+func _exit_tree() -> void:
+ macron_n = null
+ remove_context_menu_plugin(ctx_macron_n)
+
+func half_life(txt : String, type : int) -> void:
+ if !is_instance_valid(macron_n):
+ macron_n = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/main.gd").new(self)
+ macron_n.execute(txt, type)
+
+func create_new() -> void:
+ if !is_instance_valid(macron_n):
+ macron_n = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/main.gd").new(self)
+ macron_n.execute("# CODE HERE", 2)
+
+func _init() -> void:
+ var input : Variant = IDE.get_config("macro_n", "invoke_input")
+ if input is InputEvent:
+ _c_input = input
+ else:
+ _c_input = InputEventKey.new()
+ _c_input.pressed = true
+ _c_input.ctrl_pressed = true
+ _c_input.keycode = KEY_E
+ IDE.set_config("macro_n", "invoke_macro", _c_input)
+
+ input = IDE.get_config("macro_n", "invoke_macro_by_pass")
+ if input is InputEvent:
+ _cb_input = input
+ else:
+ _cb_input = InputEventKey.new()
+ _cb_input.pressed = true
+ _cb_input.ctrl_pressed = true
+ _cb_input.shift_pressed = true
+ _cb_input.keycode = KEY_E
+ IDE.set_config("macro_n", "invoke_macro_by_pass", input)
+
+ input = IDE.get_config("macro_n", "show_all_macro")
+ if input is InputEvent:
+ _g_input = input
+ else:
+ _g_input = InputEventKey.new()
+ _g_input.pressed = true
+ _g_input.alt_pressed = true
+ _g_input.keycode = KEY_END
+ IDE.set_config("macro_n", "show_all_macro", _g_input)
+
+func _input(event: InputEvent) -> void:
+ if event.is_pressed():
+ if event.is_match(_c_input):
+ ctx_macron_n.invoke_macron()
+ elif event.is_match(_cb_input):
+ ctx_macron_n.invoke_macron_bypass()
+ elif event.is_match(_g_input):
+ ctx_macron_n.show_macros()
diff --git a/addons/_Godot-IDE_/plugins/macro-n/plugin.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/plugin.gd.uid
new file mode 100644
index 0000000..775f6c9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/plugin.gd.uid
@@ -0,0 +1 @@
+uid://dx71kxr16b0k5
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd
new file mode 100644
index 0000000..65a5865
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd
@@ -0,0 +1,23 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const IFragmentDB := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+
+var _fragment_db : IFragmentDB = null
+
+func _init(fragment_db : IFragmentDB) -> void:
+ _fragment_db = fragment_db
+
+#Implement IGetAllFragments function.
+func execute() -> Array[Dictionary]:
+ return _fragment_db.get_all_fragments()
+
+#Implement IGetAllFragments function.
+func get_keys() -> PackedStringArray:
+ return _fragment_db.get_data_keys()
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd.uid
new file mode 100644
index 0000000..0c98a37
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd.uid
@@ -0,0 +1 @@
+uid://ccqfm0nk400vq
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd
new file mode 100644
index 0000000..5366c2c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd
@@ -0,0 +1,21 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+const IFragmentDB := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+
+var _fragment_db : IFragmentDB = null
+
+func _init(fragment_db : IFragmentDB) -> void:
+ _fragment_db = fragment_db
+
+
+#Implement IGetFragment function.
+func execute(shortcut : String) -> Dictionary:
+ return _fragment_db.get_fragment(shortcut)
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd.uid
new file mode 100644
index 0000000..4ddf3c8
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd.uid
@@ -0,0 +1 @@
+uid://cpk8peml78tct
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd
new file mode 100644
index 0000000..4aa060b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd
@@ -0,0 +1,17 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func get_keys() -> PackedStringArray:
+ push_error("Not supported!")
+ return []
+
+func execute() -> Array[Dictionary]:
+ push_error("Not supported!")
+ return []
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd.uid
new file mode 100644
index 0000000..143ad9e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd.uid
@@ -0,0 +1 @@
+uid://b00jdghts8a6b
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd
new file mode 100644
index 0000000..90d09b0
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd
@@ -0,0 +1,13 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func execute(shortcut : String) -> Dictionary:
+ push_error("Not supported!")
+ return {}
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd.uid
new file mode 100644
index 0000000..3421f41
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd.uid
@@ -0,0 +1 @@
+uid://br4bwcq3fxweb
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd
new file mode 100644
index 0000000..a2af172
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd
@@ -0,0 +1,13 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func execute(txt : String) -> String:
+ push_error("Not supported!")
+ return ""
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd.uid
new file mode 100644
index 0000000..2bc7cbc
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd.uid
@@ -0,0 +1 @@
+uid://d4dlryhpw7hev
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd
new file mode 100644
index 0000000..d3bc44c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd
@@ -0,0 +1,13 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func execute(txt : String) -> int:
+ push_error("Not supported!")
+ return -1
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd.uid
new file mode 100644
index 0000000..092da65
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd.uid
@@ -0,0 +1 @@
+uid://rtemrp027x42
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd
new file mode 100644
index 0000000..a330d75
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd
@@ -0,0 +1,13 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func execute(shortcut : String, description : String, content : String) -> int:
+ push_error("Not supported!")
+ return -1
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd.uid
new file mode 100644
index 0000000..3b4f509
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd.uid
@@ -0,0 +1 @@
+uid://dwci52n45a278
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd
new file mode 100644
index 0000000..f219511
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd
@@ -0,0 +1,54 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+const IFragmentDB = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+var _db : IFragmentDB
+
+func _init(db : IFragmentDB) -> void:
+ _db = db
+
+#Implement IMacro function.
+func execute(txt : String) -> String:
+ var values : Array[Dictionary] = _db.get_all_fragments()
+ if values.size() == 0:
+ return ""
+
+ var rgx : RegEx = RegEx.create_from_string("\\barg(\\d+)\\b")
+
+ for v : Dictionary in values:
+ var val : String = v["shortcut"]
+ var _rgx : RegEx = null
+ _rgx = RegEx.create_from_string(str("(?m)^",rgx.sub(val, "(.*?)", true, 0, -1),"$"))
+
+ if !is_instance_valid(_rgx) or !_rgx.is_valid():
+ continue
+
+ if null != _rgx.search(txt):
+ var input : Array = []
+ var out : Dictionary = {}
+ for x : RegExMatch in rgx.search_all(val, 0, -1):
+ if x.strings.size() > 1:
+ out[x.strings[0]] = x.strings[0]
+ input.append(x.strings[0])
+
+ var indx : int = 0
+ for x : RegExMatch in _rgx.search_all(txt, 0, -1):
+ for y : int in range(1, x.strings.size(), 1):
+ var ctnt : String = x.strings[y]
+ while indx >= input.size():
+ input.append(ctnt)
+ out[input[indx]] = ctnt
+ indx += 1
+
+ var nrgx : RegEx = RegEx.create_from_string("\\barg(\\d+)\\b")
+ var defnrgx : RegEx = RegEx.create_from_string("\\{(\\barg\\d+\\b)\\}")
+ var content : String = nrgx.sub(v["content"], "{$0}", true)
+ return defnrgx.sub(content.format(out), "$1", true)
+ return ""
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd.uid
new file mode 100644
index 0000000..ec6a55f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd.uid
@@ -0,0 +1 @@
+uid://b7jauusersf4r
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd
new file mode 100644
index 0000000..94f69f9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd
@@ -0,0 +1,66 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const LIMIT : int = 100
+
+const IFragmentDB = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+var _db : IFragmentDB
+
+func _init(db : IFragmentDB) -> void:
+ _db = db
+
+#Implement IMacro function.
+func execute(txt : String) -> String:
+ var values : Array[Dictionary] = _db.get_all_fragments()
+ if values.size() == 0:
+ return ""
+
+ var rgx : RegEx = RegEx.create_from_string("\\barg(\\d+)\\b")
+ for v : Dictionary in values:
+ var val : String = v["shortcut"]
+ var _rgx : RegEx = null
+ _rgx = RegEx.create_from_string(str("\\s*",rgx.sub(val, "(.*?)", true, 0, -1),"\\s*"))
+
+ if !is_instance_valid(_rgx) or !_rgx.is_valid():
+ continue
+
+ if null != _rgx.search(txt):
+ var input : Array = []
+ var out : Dictionary = {}
+ for x : RegExMatch in rgx.search_all(val, 0, -1):
+ if x.strings.size() > 1:
+ out[x.strings[0]] = x.strings[0]
+ input.append(x.strings[0])
+
+ var indx : int = 0
+ for x : RegExMatch in _rgx.search_all(txt, 0, -1):
+ for y : int in range(1, x.strings.size(), 1):
+ var ctnt : String = x.strings[y]
+ while indx >= input.size():
+ input.append(ctnt)
+ out[input[indx]] = ctnt
+ indx += 1
+
+ var nrgx : RegEx = RegEx.create_from_string("\\barg(\\d+)\\b")
+ var defnrgx : RegEx = RegEx.create_from_string("\\{(\\barg\\d+\\b)\\}")
+ var content : String = nrgx.sub(v["content"], "{$0}", true)
+
+ var result : String = _rgx.sub(txt, defnrgx.sub(content.format(out), "$1", true), true)
+ var new_result : String = execute(result)
+ var _out : int = LIMIT
+
+ while !new_result.is_empty() and result != new_result and _out > 0:
+ result = new_result
+ new_result = execute(new_result)
+ _out -= 1
+
+ if _out == 0:
+ printerr("Macro overflow error!")
+ return result
+ return ""
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd.uid
new file mode 100644
index 0000000..285d0ef
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd.uid
@@ -0,0 +1 @@
+uid://curvq1o31f5cx
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd
new file mode 100644
index 0000000..20d4947
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd
@@ -0,0 +1,28 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+func save_fragment(shortcut : String, description : String, full_text : String) -> int:
+ push_error("Not supported!")
+ return -1
+
+func get_fragment(shortcut : String) -> Dictionary:
+ push_error("Not supported!")
+ return {}
+
+func get_all_fragments() -> Array[Dictionary]:
+ push_error("Not supported!")
+ return []
+
+func get_data_keys() -> PackedStringArray:
+ push_error("Not supported!")
+ return []
+
+func remove(shortcut : String) -> int:
+ push_error("Not supported!")
+ return -1
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd.uid
new file mode 100644
index 0000000..74e5b24
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd.uid
@@ -0,0 +1 @@
+uid://bqt2ahh581vvv
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd
new file mode 100644
index 0000000..e38bfae
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd
@@ -0,0 +1,19 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const IFragmentDB := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+
+var _fragment_db : IFragmentDB = null
+
+func _init(fragment_db : IFragmentDB) -> void:
+ _fragment_db = fragment_db
+
+#Implement IRemoveFragment function.
+func execute(txt : String) -> int:
+ return _fragment_db.remove(txt)
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd.uid
new file mode 100644
index 0000000..2f32e93
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/remove_fragment.gd.uid
@@ -0,0 +1 @@
+uid://cts7bspkw8xjg
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd b/addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd
new file mode 100644
index 0000000..3a4aa8a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd
@@ -0,0 +1,27 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const IFragmentDB := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+
+var _fragment_db : IFragmentDB = null
+
+func _init(fragment_db : IFragmentDB) -> void:
+ _fragment_db = fragment_db
+
+
+
+#Implement ISerializerFragments function.
+func execute(shortcut : String, description : String, content : String) -> int:
+ description = description.strip_edges()
+ shortcut = shortcut.strip_edges()
+
+ var result : int = _fragment_db.save_fragment(shortcut, description, content)
+ if OK != result:
+ push_error("On error on try save macro fragment!")
+ return result
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd.uid
new file mode 100644
index 0000000..816b3ba
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd.uid
@@ -0,0 +1 @@
+uid://duou0e0hh4e7t
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/core/fragment.gd b/addons/_Godot-IDE_/plugins/macro-n/src/core/fragment.gd
new file mode 100644
index 0000000..3224847
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/core/fragment.gd
@@ -0,0 +1,13 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+var shortcut : String
+var tittle : String
+var description : String
+var text : String
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/core/fragment.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/core/fragment.gd.uid
new file mode 100644
index 0000000..81bf762
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/core/fragment.gd.uid
@@ -0,0 +1 @@
+uid://dwmbc85wlymty
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/Fragments.tscn b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/Fragments.tscn
new file mode 100644
index 0000000..fd1e9eb
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/Fragments.tscn
@@ -0,0 +1,81 @@
+[gd_scene load_steps=5 format=3 uid="uid://ckn2qdpg5fs5y"]
+
+[ext_resource type="Script" uid="uid://bio4l72bwqcsi" path="res://addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd" id="1_4jc8e"]
+[ext_resource type="Script" uid="uid://b4ypyfc1c0ybj" path="res://addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd" id="2_k3ugs"]
+[ext_resource type="Texture2D" uid="uid://ckc3yk6f8y3ob" path="res://addons/_Godot-IDE_/shared_resources/func_public.svg" id="2_r7fa2"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_r7fa2"]
+content_margin_left = 5.0
+content_margin_top = 5.0
+content_margin_right = 5.0
+content_margin_bottom = 5.0
+bg_color = Color(0.1155, 0.132, 0.1595, 1)
+corner_detail = 1
+anti_aliasing = false
+
+[node name="Fragments" type="Window" node_paths=PackedStringArray("container")]
+title = "Show All Macros"
+initial_position = 4
+size = Vector2i(1536, 864)
+wrap_controls = true
+transient = true
+transient_to_focused = true
+exclusive = true
+script = ExtResource("1_4jc8e")
+container = NodePath("PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/GridContainer")
+
+[node name="PanelContainer" type="PanelContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_r7fa2")
+
+[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 8
+theme_override_constants/margin_top = 8
+theme_override_constants/margin_right = 8
+theme_override_constants/margin_bottom = 8
+
+[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+text = "All Shortucts"
+horizontal_alignment = 1
+uppercase = true
+
+[node name="HSeparator2" type="HSeparator" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+
+[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="GridContainer" type="GridContainer" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+columns = 7
+
+[node name="HSeparator" type="HSeparator" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 16
+
+[node name="create" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
+layout_mode = 2
+text = "Create New"
+icon = ExtResource("2_r7fa2")
+script = ExtResource("2_k3ugs")
+
+[node name="close" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Close"
+script = ExtResource("2_k3ugs")
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/Edit.tscn b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/Edit.tscn
new file mode 100644
index 0000000..3133bcc
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/Edit.tscn
@@ -0,0 +1,83 @@
+[gd_scene load_steps=5 format=3 uid="uid://dt1pjig5ml6we"]
+
+[ext_resource type="Script" uid="uid://ctoxe1fxxiuc1" path="res://addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd" id="1_0vdlq"]
+[ext_resource type="Script" uid="uid://b4ypyfc1c0ybj" path="res://addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd" id="2_h73ec"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_leugs"]
+content_margin_left = 5.0
+content_margin_top = 5.0
+content_margin_right = 5.0
+content_margin_bottom = 5.0
+bg_color = Color(0.1155, 0.132, 0.1595, 1)
+corner_detail = 1
+anti_aliasing = false
+
+[sub_resource type="GDScriptSyntaxHighlighter" id="GDScriptSyntaxHighlighter_h73ec"]
+
+[node name="Edit" type="Window" node_paths=PackedStringArray("update_btn", "base_edit", "container")]
+position = Vector2i(0, 36)
+size = Vector2i(500, 500)
+transient = true
+transient_to_focused = true
+exclusive = true
+script = ExtResource("1_0vdlq")
+update_btn = NodePath("MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/update")
+base_edit = NodePath("MarginContainer/MarginContainer/CodeEdit")
+container = NodePath("MarginContainer/MarginContainer/VBoxContainer/ScrollContainer")
+
+[node name="MarginContainer" type="PanelContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_leugs")
+
+[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="CodeEdit" type="CodeEdit" parent="MarginContainer/MarginContainer"]
+visible = false
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+syntax_highlighter = SubResource("GDScriptSyntaxHighlighter_h73ec")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+text = "Edit Content"
+horizontal_alignment = 1
+
+[node name="HSeparator" type="HSeparator" parent="MarginContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+
+[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="HSeparator2" type="HSeparator" parent="MarginContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 12
+
+[node name="close" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Close"
+script = ExtResource("2_h73ec")
+
+[node name="update" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Update"
+script = ExtResource("2_h73ec")
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd
new file mode 100644
index 0000000..3789061
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd
@@ -0,0 +1,100 @@
+@tool
+extends Window
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+@export var update_btn : Button
+@export var base_edit : CodeEdit
+@export var code_edit : CodeEdit
+@export var container : Control
+
+var _origin : Object = null
+
+var _code : String = ""
+
+func _get_edit() -> CodeEdit:
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var _editor : Control = editor.get_current_editor().get_base_editor()
+ if _editor is CodeEdit:
+ return _editor.duplicate(0)
+
+ return CodeEdit.new()
+
+func _on_change_txt() -> void:
+ base_edit.text = code_edit.text
+
+func _ready() -> void:
+ close_requested.connect(close)
+
+ var res : CodeHighlighter = ResourceLoader.load("uid://cyenwroye7tue").new()
+
+ res.set_base(base_edit.syntax_highlighter)
+ res.set_rgx(RegEx.create_from_string("\\barg\\d+\\b"))
+
+ if code_edit == null:
+ code_edit = _get_edit()
+ container.add_child(code_edit)
+ code_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ code_edit.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+
+ code_edit.syntax_highlighter = res
+ code_edit.text_changed.connect(_on_change_txt)
+
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return
+ get_child(0). add_theme_stylebox_override(&"panel", control.get_theme_stylebox(&"panel", &""))
+
+func set_origin(o : Object) -> void:
+ _origin = o
+
+func close() -> void:
+ queue_free()
+
+func update() -> void:
+ var pop : ConfirmationDialog = ConfirmationDialog.new()
+ pop.title = "Macro-N"
+ pop.size.x = 350
+
+ pop.dialog_text = "Are you sure update?"
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ pop.confirmed.connect(_on_confirm)
+ EditorInterface.popup_dialog_centered(pop)
+
+func _on_confirm() -> void:
+ _origin.set_meta(&"content", code_edit.text)
+
+ var pop : AcceptDialog = AcceptDialog.new()
+ pop.title = "Macro-N"
+ pop.size.x = 350
+ pop.dialog_text = "Macro content update!\nRemember press 'UPDATE' button for save!"
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ pop.close_requested.connect(close)
+ pop.confirmed.connect(close)
+ EditorInterface.popup_dialog_centered(pop)
+
+func _on_change() -> void:
+ update_btn.disabled = _code == code_edit.text
+
+func set_code(code : String) -> void:
+ _code = code
+ base_edit.text = _code
+ code_edit.text = _code
+
+ code_edit.text_changed.connect(_on_change)
+ update_btn.disabled = true
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd.uid
new file mode 100644
index 0000000..1d21ac5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/edit.gd.uid
@@ -0,0 +1 @@
+uid://ctoxe1fxxiuc1
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd
new file mode 100644
index 0000000..c31ab8b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd
@@ -0,0 +1,382 @@
+@tool
+extends Window
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+const IGetAllFragments = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd")
+const IRemoveFragment = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IRemoveFragment.gd")
+const ISerializerFragments = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd")
+
+const SCRIPT = preload("res://addons/_Godot-IDE_/shared_resources/Script.svg")
+const METHOD_OVERRIDE = preload("res://addons/_Godot-IDE_/shared_resources/MethodOverride.svg")
+const DOT = preload("res://addons/_Godot-IDE_/shared_resources/dot.svg")
+
+const EDIT = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/component/Edit.tscn")
+
+signal on_create()
+
+@export var container : Node = null
+
+var _GetAllFragments : IGetAllFragments = null
+var _RemoveFragment : IRemoveFragment = null
+var _SerializerFragments : ISerializerFragments = null
+
+func _ready() -> void:
+ set_process(false)
+ size = IDE.get_screen_size(0.8)
+ size.x = maxi(size.x, 300)
+ size.y = maxi(size.y, 300)
+ about_to_popup.connect(_about_to_popup)
+ close_requested.connect(close)
+
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return
+ get_child(0). add_theme_stylebox_override(&"panel", control.get_theme_stylebox(&"panel", &""))
+
+
+
+func close() -> void:
+ queue_free()
+
+func _about_to_popup() -> void:
+ set_process(false)
+ _make_components(container)
+
+func set_dependencies(res : Array[Variant]) -> void:
+ for o : Variant in res:
+ if o is IGetAllFragments:
+ _GetAllFragments = o
+ elif o is IRemoveFragment:
+ _RemoveFragment = o
+ elif o is ISerializerFragments:
+ _SerializerFragments = o
+ if !is_instance_valid(_RemoveFragment) or !is_instance_valid(_GetAllFragments) or !is_instance_valid(_SerializerFragments):
+ push_warning("Not defined all dependencies!")
+
+func _make_component(root : Node, data : Dictionary) -> void:
+ var shortcut : LineEdit = LineEdit.new()
+ var copy_shortcut : Button = Button.new()
+
+
+ var description : LineEdit = LineEdit.new()
+ var copy_clipboard : Button = Button.new()
+ var show_content : Button = Button.new()
+ var update_show_content : Button = Button.new()
+ var erase_show_content : Button = Button.new()
+
+
+ shortcut.text = data["shortcut"]
+ description.text = data["description"]
+ show_content.text = ""
+ update_show_content.text = ""
+ erase_show_content.text = ""
+ copy_shortcut.text = ""
+ copy_clipboard.text = ""
+
+ copy_shortcut.disabled = shortcut.text.length() < 1
+ copy_shortcut.tooltip_text = "Copy Macro Shorcut Button"
+ copy_clipboard.tooltip_text = "Copy Macro Content Button"
+
+ show_content.alignment = HORIZONTAL_ALIGNMENT_CENTER
+ update_show_content.alignment = HORIZONTAL_ALIGNMENT_CENTER
+ erase_show_content.alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ shortcut.set_meta(&"origin", shortcut.text)
+ description.set_meta(&"origin", description.text)
+ show_content.set_meta(&"origin", data["content"])
+ show_content.set_meta(&"content", data["content"])
+
+ shortcut.tooltip_text = shortcut.text
+ description.tooltip_text = description.text
+ show_content.tooltip_text = show_content.text
+
+ shortcut.text_changed.connect(_on_text.bind(shortcut))
+ description.text_changed.connect(_on_text.bind(description))
+
+ show_content.icon = SCRIPT
+ update_show_content.icon = METHOD_OVERRIDE
+ erase_show_content.icon = DOT
+ copy_clipboard.icon = DOT
+ copy_shortcut.icon = DOT
+
+ copy_clipboard.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ copy_shortcut.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ erase_show_content.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ update_show_content.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ show_content.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ copy_shortcut.modulate = Color.GREEN
+ copy_clipboard.modulate = Color.SKY_BLUE
+ erase_show_content.modulate = Color.RED
+
+ copy_shortcut.pressed.connect(_copy.bind(shortcut))
+ copy_clipboard.pressed.connect(_copy.bind(show_content))
+ show_content.pressed.connect(_on_content.bind(show_content))
+ update_show_content.pressed.connect(_on_update.bind(shortcut, description, show_content))
+ erase_show_content.pressed.connect(_on_erase.bind(shortcut))
+
+ root.add_child(shortcut)
+ root.add_child(copy_shortcut)
+ root.add_child(description)
+ root.add_child(copy_clipboard)
+ root.add_child(show_content)
+ root.add_child(update_show_content)
+ root.add_child(erase_show_content)
+
+ shortcut.custom_minimum_size.x = 350.0
+ description.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+
+func _copy(content : Node) -> void:
+ var txt_content : String = ""
+ var type : String = ""
+ if content is Button:
+ if !content.has_meta(&"content"):
+ printerr("Not has content!")
+ return
+
+ type = "Content"
+
+ txt_content = content.get_meta(&"content")
+
+ elif content is LineEdit:
+
+ if content.text.length() == 0:
+ printerr("Not has shortcut content!")
+ return
+
+ type = "Shortcut"
+ txt_content = content.text
+
+ var copy : bool = false
+ var txt : String = ""
+
+ if txt_content.length() > 0:
+ DisplayServer.clipboard_set(txt_content)
+ txt = ("Macro {0} copied to clipboard!".format([type]))
+ else:
+ txt = ("Error, Macro {0} not copied to clipboard!".format([type]))
+
+ var pop : AcceptDialog = AcceptDialog.new()
+
+ pop.title = "Macro-N"
+ pop.dialog_text = txt.capitalize()
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ EditorInterface.popup_dialog_centered(pop)
+
+var _edit : Window
+
+func _on_content(content : Node) -> void:
+ if content.has_meta(&"content"):
+ var txt : String = content.get_meta(&"content")
+
+ if !is_instance_valid(_edit):
+ _edit = EDIT.instantiate()
+ add_child(_edit)
+ _edit.set_origin(content)
+ _edit.set_code(txt)
+ _edit.popup_centered()
+ else:
+ printerr("Not defined content!! {0}".format([content]))
+
+func _on_update(shortcut : LineEdit, description : LineEdit, show_content : Node) -> void:
+ for x : Node in [shortcut, description, show_content]:
+ if !x.has_meta(&"origin"):
+ printerr("Not defined origin content! {0}".format([x]))
+ return
+
+ var changes : PackedStringArray = []
+
+ if shortcut.text != shortcut.get_meta(&"origin"):
+ changes.append("shortcut".capitalize())
+ if description.text != description.get_meta(&"origin"):
+ changes.append("description".capitalize())
+ if show_content.get_meta(&"content") != show_content.get_meta(&"origin"):
+ changes.append("macro_content".capitalize())
+
+
+ var pop : AcceptDialog = null
+
+ if changes.size() == 0:
+ pop = AcceptDialog.new()
+ else:
+ pop = ConfirmationDialog.new()
+ pop.confirmed.connect(_on_confirm_update.bind(shortcut, description, show_content))
+
+ pop.title = "Macro-N"
+
+ pop.dialog_text = "None changes for be update!"
+
+ if changes.size() > 0 :
+ pop.dialog_text = "Changes to update:\n{0}".format([", ".join(changes)])
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ EditorInterface.popup_dialog_centered(pop)
+
+
+func _on_confirm_update(shortcut : LineEdit, description : LineEdit, show_content : Control) -> void:
+ var done : bool = false
+ var origin : String = shortcut.get_meta(&"origin")
+ if shortcut.text != origin:
+ if OK == _RemoveFragment.execute(origin):
+ if OK == _SerializerFragments.execute(shortcut.text, description.text, show_content.get_meta(&"content")):
+ shortcut.set_meta(&"origin", shortcut.text)
+ description.set_meta(&"origin", description.text)
+ done = true
+ else:
+ printerr("Error, can not save!")
+ else:
+ if OK == _SerializerFragments.execute(shortcut.text, description.text, show_content.get_meta(&"content")):
+ shortcut.set_meta(&"origin", shortcut.text)
+ description.set_meta(&"origin", description.text)
+ done = true
+ else:
+ printerr("Error, can not save!")
+
+ var pop : AcceptDialog = AcceptDialog.new()
+ pop.title = "Macro-N"
+
+ if done:
+ pop.dialog_text = "Macro updated success!"
+ else:
+ pop.dialog_text = "Error on trying update Macro!"
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ EditorInterface.popup_dialog_centered(pop)
+
+ _update()
+
+func _update() -> void:
+ set_process(true)
+
+func _process(delta: float) -> void:
+ set_process(false)
+ _make_components(container)
+
+func _on_erase(item : LineEdit) -> void:
+ var id : String = ""
+ if item.has_meta(&"origin"):
+ id = item.get_meta(&"origin")
+
+ var pop : ConfirmationDialog = ConfirmationDialog.new()
+ pop.title = "Macro-N"
+
+ if id.is_empty():
+ pop.dialog_text = "Are you sure to remove?"
+ else:
+ pop.dialog_text = "Are you sure to remove?\n[{0}]".format([id])
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ pop.confirmed.connect(_on_confirm_erase.bind(item))
+
+ EditorInterface.popup_dialog_centered(pop)
+
+func _on_confirm_erase(item : LineEdit) -> void:
+ var id : String = ""
+ if item.has_meta(&"origin"):
+ id = item.get_meta(&"origin")
+ else:
+ id = item.text
+ if _RemoveFragment.execute(id) == OK:
+ var pop : AcceptDialog = AcceptDialog.new()
+ pop.title = "Macro-N"
+
+ pop.dialog_text = "Removed Macro Success!"
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ EditorInterface.popup_dialog_centered(pop)
+
+ _update()
+ else:
+ var pop : AcceptDialog = AcceptDialog.new()
+ pop.title = "Macro-N"
+
+ if id.is_empty():
+ pop.dialog_text = "Error on trying find Macro for be remove!"
+ else:
+ pop.dialog_text = "Error on trying remove Macro:\n{0}".format([id])
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ EditorInterface.popup_dialog_centered(pop)
+
+
+func _make_tittle_component(root : Node) -> void:
+ var shortcut : Label = Label.new()
+ var copy_shortcut : Label = Label.new()
+ var description : Label = Label.new()
+ var show_content : Label = Label.new()
+ var copy_content : Label = Label.new()
+ var update_show_content : Label = Label.new()
+ var erase_show_content : Label = Label.new()
+
+ copy_shortcut.text = "Copy Shorcut"
+ shortcut.text = "Shortcut"
+ description.text = "Description"
+ show_content.text = "Show Content"
+ update_show_content.text = "Update"
+ erase_show_content.text = "Erase"
+ copy_content.text = "Copy Content"
+
+ for x : Label in [shortcut, copy_shortcut, description, copy_content, show_content, update_show_content, erase_show_content]:
+ x.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ root.add_child(x)
+
+func _on_text(txt : String, item : Control) -> void:
+ item.set(&"tooltip_text", txt)
+
+func _make_components(root : Node) -> void:
+
+ if !is_instance_valid(root):
+ printerr("Not define container!")
+ return
+
+ for x : Node in root.get_children():
+ x.queue_free()
+
+ var objects : Array[Dictionary] = _GetAllFragments.execute()
+ if objects.size() < 1:
+ var label : Label = Label.new()
+ label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ label.size_flags_vertical = Control.SIZE_EXPAND_FILL
+ label.text = "You don't have any macros saved yet!"
+ root.add_child(label)
+ return
+
+ _make_tittle_component(root)
+
+ for o : Dictionary in objects:
+ var val : String = o["shortcut"]
+ if val.is_empty():
+ continue
+ _make_component(root, o)
+
+func create() -> void:
+ on_create.emit()
+ close()
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd.uid
new file mode 100644
index 0000000..e135c0d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/Fragments/fragments.gd.uid
@@ -0,0 +1 @@
+uid://bio4l72bwqcsi
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd b/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd
new file mode 100644
index 0000000..0ebf27f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd
@@ -0,0 +1,230 @@
+@tool
+extends Window
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+const IGetAllFragments := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd")
+const IGetFragment := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd")
+const ISerializerFragment := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd")
+
+const EDITOR_KEY : String = "content"
+const ID_KEY : String = "shortcut"
+
+@export var fragment_data_container : Control = null
+@export var base_editor : CodeEdit = null
+
+var _GetAllFragments : IGetAllFragments = null
+var _GetFragment : IGetFragment = null
+var _SerializerFragment : ISerializerFragment = null
+
+
+var _content_container : CodeEdit = null
+var _id_container : LineEdit = null
+
+func _ready() -> void:
+ close_requested.connect(close)
+
+ var control : Control = EditorInterface.get_base_control()
+ if !control:
+ return
+ get_child(0). add_theme_stylebox_override(&"panel", control.get_theme_stylebox(&"panel", &""))
+
+
+func close() -> void:
+ queue_free()
+
+func set_dependencies(dependencies : Array) -> void:
+ for dp : Variant in dependencies:
+ if dp is IGetAllFragments:
+ _GetAllFragments = dp
+ elif dp is IGetFragment:
+ _GetFragment = dp
+ elif dp is ISerializerFragment:
+ _SerializerFragment = dp
+ for x : Object in [_GetAllFragments, _GetFragment, _SerializerFragment]:
+ if !is_instance_valid(x):
+ push_error("[Macro-N] Error on get dependencies!")
+ return
+ _setup()
+
+func accept() -> void:
+ var pray2god : bool = false
+ var errors : PackedStringArray = []
+
+ if !is_instance_valid(_id_container) or !is_instance_valid(_content_container):
+ pray2god = true
+ else:
+ _id_container.text = _id_container.text.strip_edges()
+ for x : Node in fragment_data_container.get_children():
+ if x is LineEdit or x is CodeEdit:
+ if x.text.is_empty():
+ pray2god = true
+ errors.append(x.name)
+
+ if pray2god:
+ var pop : AcceptDialog = AcceptDialog.new()
+ pop.title = "🔥 This is fine..."
+ pop.size.x = 500
+
+ if errors.size() > 0:
+ pop.dialog_text = "Check that the values are set correctly:\n{0}".format([", ".join(errors)])
+ else:
+ pop.dialog_text = "An error occurred during processing!" #Get me out, Latom.
+
+ for x : Node in pop.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+ EditorInterface.popup_dialog_centered(pop)
+ return
+
+ var shortcut : String = _id_container.text
+
+ var text : String = "Create new Marcro? \n'{0}'".format([shortcut])
+
+ var window : ConfirmationDialog = ConfirmationDialog.new()
+ window.title = "Hey, Listen!"
+
+ window.get_ok_button().self_modulate = Color.GREEN
+ window.get_cancel_button().self_modulate = Color.RED
+
+ if !_GetFragment.execute(shortcut)["shortcut"].is_empty():
+ text = "The Macro '{0}' already exists!\nyou want to overwrite?".format([shortcut])
+ window.get_ok_button().self_modulate = Color.ORANGE
+ window.ok_button_text = "Overwrite"
+
+ window.dialog_text = text
+
+ for x : Node in window.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+
+
+ window.confirmed.connect(_save)
+ EditorInterface.popup_dialog_centered(window)
+
+func _save() -> void:
+ if !is_instance_valid(_id_container) or !is_instance_valid(_content_container):
+ return
+
+ var data : Dictionary[String, String] = {}
+ for x : Node in fragment_data_container.get_children():
+ if x is LineEdit or x is CodeEdit:
+ data[x.name] = x.text
+
+ var keys : PackedStringArray = _GetAllFragments.get_keys()
+ if data.has_all(_GetAllFragments.get_keys()):
+ var text : String = "The Macro {0} is saved!".format([data[keys[0]]])
+ var result : int = _SerializerFragment.execute(
+ data[keys[0]],
+ data[keys[1]],
+ data[keys[2]]
+ )
+ if OK != result:
+ text = "An error on try save Macro: {0}".format([data[keys[0]]])
+ else:
+ close.call_deferred()
+
+ var window : AcceptDialog = AcceptDialog.new()
+ window.title = "Message Result"
+ window.dialog_text = text
+
+ for x : Node in window.get_children(true):
+ if x is Label:
+ x.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ EditorInterface.popup_dialog_centered(window)
+ else:
+ push_error("[Macro-N] Error, not valid keys!")
+ close()
+
+func set_content(txt : String) -> void:
+ if is_instance_valid(_content_container):
+ base_editor.text = parse_text(txt)
+ _content_container.text = base_editor.text
+ else:
+ push_warning("Can not found editor container!")
+
+func parse_text(txt : String) -> String:
+ var split : PackedStringArray = txt.split("\n", true, 0)
+ var maxt : Array[int] = [0, 0]
+
+ while split.size() > 0 and split[0].strip_edges().length() == 0:
+ split.remove_at(0)
+
+ if split.size() == 0:
+ return ""
+
+ for xline : int in range(split.size()):
+ var line : String = split[xline]
+ var indx : int = 0
+ var cline : int = mini(xline, 1)
+ while line.length() > indx and (line[indx] == '\t' or line[indx] == ' '):
+ indx += 1
+ maxt[cline] = maxi(maxt[cline], indx)
+
+ if maxt[0] < maxt[1]:
+ var st : int = maxt[0]
+ for xline : int in range(split.size()):
+ split[xline] = split[xline].substr(st, -1)
+
+ return "\n".join(split)
+
+func _on_change_txt() -> void:
+ base_editor.text = _content_container.text
+
+func _get_edit() -> CodeEdit:
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var _editor : Control = editor.get_current_editor().get_base_editor()
+ if _editor is CodeEdit:
+ return _editor.duplicate(0)
+
+ return CodeEdit.new()
+
+func _setup() -> void:
+ var keys : PackedStringArray = _GetAllFragments.get_keys()
+ for x : Node in fragment_data_container.get_children():
+ x.name = "_" + x.name
+ x.queue_free()
+
+ for k : String in keys:
+ if k == EDITOR_KEY:
+ var control : CodeEdit = _get_edit()
+ var res : CodeHighlighter = ResourceLoader.load("uid://cyenwroye7tue").new()
+
+ res.set_base(base_editor.syntax_highlighter)
+ res.set_rgx(RegEx.create_from_string("\\barg\\d+\\b"))
+
+ fragment_data_container.add_child(control)
+ control.syntax_highlighter = res
+ control.text_changed.connect(_on_change_txt)
+
+ control.name = k
+
+ control.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ control.size_flags_vertical = Control.SIZE_EXPAND_FILL
+ _content_container = control
+ continue
+
+ var tittle: Label = Label.new()
+ var edit : LineEdit = LineEdit.new()
+ tittle.text = k.capitalize()
+ edit.placeholder_text = "Please set {0} value".format([tittle.text])
+
+ edit.name = k
+
+ fragment_data_container.add_child(tittle)
+ fragment_data_container.add_child(edit)
+
+ tittle.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+
+ if k == ID_KEY:
+ _id_container = edit
+
+ size = IDE.get_screen_size(0.75)
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd.uid
new file mode 100644
index 0000000..55dbd70
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd.uid
@@ -0,0 +1 @@
+uid://bnkrnw0nf5fhm
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.tscn b/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.tscn
new file mode 100644
index 0000000..bc2ed83
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.tscn
@@ -0,0 +1,82 @@
+[gd_scene load_steps=5 format=3 uid="uid://b25j4mq6e1fso"]
+
+[ext_resource type="Script" uid="uid://bnkrnw0nf5fhm" path="res://addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.gd" id="1_axr5e"]
+[ext_resource type="Script" uid="uid://b4ypyfc1c0ybj" path="res://addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd" id="2_x0cqq"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3tb81"]
+content_margin_left = 6.0
+content_margin_top = 6.0
+content_margin_right = 6.0
+content_margin_bottom = 6.0
+bg_color = Color(0.1155, 0.132, 0.1595, 1)
+corner_detail = 1
+anti_aliasing = false
+
+[sub_resource type="GDScriptSyntaxHighlighter" id="GDScriptSyntaxHighlighter_x0cqq"]
+
+[node name="SweetOne" type="Window" node_paths=PackedStringArray("fragment_data_container", "base_editor")]
+title = "Plugin Macro-N"
+initial_position = 4
+size = Vector2i(500, 500)
+exclusive = true
+script = ExtResource("1_axr5e")
+fragment_data_container = NodePath("PanelContainer/MarginContainer/VBoxContainer/VBoxContainer")
+base_editor = NodePath("PanelContainer/MarginContainer/CodeEdit")
+
+[node name="PanelContainer" type="PanelContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_styles/panel = SubResource("StyleBoxFlat_3tb81")
+
+[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="CodeEdit" type="CodeEdit" parent="PanelContainer/MarginContainer"]
+visible = false
+layout_mode = 2
+syntax_highlighter = SubResource("GDScriptSyntaxHighlighter_x0cqq")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+text = "Macro-N"
+horizontal_alignment = 1
+
+[node name="HSeparator" type="HSeparator" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+
+[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="HSeparator2" type="HSeparator" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 16
+
+[node name="close" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
+modulate = Color(1, 0.392157, 1, 1)
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Close"
+script = ExtResource("2_x0cqq")
+
+[node name="accept" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
+modulate = Color(0.392157, 1, 1, 1)
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Accept"
+script = ExtResource("2_x0cqq")
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd b/addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd
new file mode 100644
index 0000000..276416a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd
@@ -0,0 +1,44 @@
+@tool
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func execute() -> String:
+ var text : String = ""
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ var sc : Script = editor.get_current_script()
+ if sc:
+ var base : ScriptEditorBase = editor.get_current_editor()
+ var control : Control = base.get_base_editor()
+ if control is CodeEdit:
+ var last_line : int = -1
+ for x : int in control.get_caret_count():
+ var current : String = control.get_selected_text(x)
+ if current.is_empty():
+ continue
+ var from : int = control.get_selection_from_column(x)
+ var line : int = control.get_caret_line(x)
+ if from > 0:
+ var txt : String = control.get_line(line)
+ if txt.length() > from:
+ while from > -1:
+ var chars : String = txt[from]
+ if chars == '\t':
+ current = '\t' + current
+ elif chars == ' ':
+ current = ' ' + current
+ from -= 1
+ if last_line == -1:
+ text += current
+ elif last_line < line:
+ text = str(text ,'\n', current)
+ else:
+ text = str(current ,'\n', text)
+ last_line = line
+ return text
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd.uid
new file mode 100644
index 0000000..a539339
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/controller/ShowTextController.gd.uid
@@ -0,0 +1 @@
+uid://ctahuhicmqk8i
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd
new file mode 100644
index 0000000..8fb12ad
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd
@@ -0,0 +1,13 @@
+@tool
+extends Button
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+
+func _pressed() -> void:
+ if owner and owner.has_method(name):
+ owner.call(name)
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd.uid
new file mode 100644
index 0000000..ad32350
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/Button.gd.uid
@@ -0,0 +1 @@
+uid://b4ypyfc1c0ybj
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/syntax.gd b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/syntax.gd
new file mode 100644
index 0000000..6f86d6b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/syntax.gd
@@ -0,0 +1,37 @@
+extends CodeHighlighter
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+var _enabled : bool = false
+var rgx : RegEx = null
+
+var _base : SyntaxHighlighter = null
+
+func set_base(b : SyntaxHighlighter) -> void:
+ _base = b
+ _enabled = is_instance_valid(_base) and is_instance_valid(rgx)
+
+func set_rgx(r : RegEx) -> void:
+ rgx = r
+ _enabled = is_instance_valid(_base) and is_instance_valid(rgx)
+
+func _get_line_syntax_highlighting(line : int) -> Dictionary:
+ var out : Dictionary = {}
+ if _enabled:
+ out = _base.get_line_syntax_highlighting(line)
+
+ var line_text: String = get_text_edit().get_line(line)
+ for x : RegExMatch in rgx.search_all(line_text):
+ var start : int = x.get_start()
+ var end : int = x.get_end()
+ _add_color_region(out, start, end, Color.GOLD)
+
+ return out
+
+func _add_color_region(data: Dictionary, start_col: int, end_col: int, color: Color):
+ for x : int in range(start_col, end_col, 1):
+ data[x] = {"color": color}
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/syntax.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/syntax.gd.uid
new file mode 100644
index 0000000..862c2c5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/gui/util/syntax.gd.uid
@@ -0,0 +1 @@
+uid://cyenwroye7tue
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/main.gd b/addons/_Godot-IDE_/plugins/macro-n/src/main.gd
new file mode 100644
index 0000000..691c499
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/main.gd
@@ -0,0 +1,80 @@
+extends RefCounted
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const IFragmentDB := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd")
+const IGetAllFragments := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetAllFragments.gd")
+const IGetFragment := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IGetFragment.gd")
+const ISerializerFragment := preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/ISerializerFragments.gd")
+const IMacro = preload("res://addons/_Godot-IDE_/plugins/macro-n/src/app/in/IMacro.gd")
+
+var _root : Node = null
+var _node : Window = null
+
+func _init(root : Node) -> void:
+ _root = root
+
+func _cut() -> void:
+ var ip : InputEventKey = InputEventKey.new()
+ ip.ctrl_pressed = true
+ ip.pressed = true
+ ip.keycode = KEY_X
+ Engine.get_main_loop().root.push_input(ip)
+
+func _paste(txt : String) -> void:
+ DisplayServer.clipboard_set(txt)
+
+ var cip : InputEventKey = InputEventKey.new()
+ cip.ctrl_pressed = true
+ cip.pressed = true
+ cip.keycode = KEY_V
+ Engine.get_main_loop().root.push_input(cip)
+
+func execute(txt : String, type : int) -> void:
+ if type == 0 or type == 1:
+ var FragmentDB : IFragmentDB = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd").new()
+ var macro : IMacro = null
+ if type == 0:
+ macro = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro.gd").new(FragmentDB)
+ else:
+ macro = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/invoke_macro_bypass.gd").new(FragmentDB)
+ var content : String = macro.execute(txt)
+ if content.length() == 0:
+ return
+
+ var sc : ScriptEditor = EditorInterface.get_script_editor()
+ var node : Node = sc.get_current_editor()
+ if node:
+ node = node.get_base_editor()
+ if node is CodeEdit:
+ if node.get_caret_count() > 0:
+ _cut()
+ _paste(content)
+ else:
+ if is_instance_valid(_node):
+ _node.popup_centered()
+ return
+
+ var GUI : PackedScene = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/gui/SweetOne/SweetOne.tscn")
+
+ var FragmentDB : IFragmentDB = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd").new()
+ var GetAllFragments : IGetAllFragments = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/get_all_fragments.gd").new(FragmentDB)
+ var GetFragment : IGetFragment = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/get_fragment.gd").new(FragmentDB)
+ var SerializerFragment : ISerializerFragment = ResourceLoader.load("res://addons/_Godot-IDE_/plugins/macro-n/src/app/save_fragment.gd").new(FragmentDB)
+
+ _node = GUI.instantiate()
+ _root.add_child(_node)
+
+ _node.set_dependencies([
+ GetAllFragments,
+ GetFragment,
+ SerializerFragment
+ ])
+
+ _node.set_content(txt)
+
+ _node.popup_centered()
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/main.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/main.gd.uid
new file mode 100644
index 0000000..f6cdcca
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/main.gd.uid
@@ -0,0 +1 @@
+uid://ddggr4djfu4ft
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd b/addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd
new file mode 100644
index 0000000..2657781
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd
@@ -0,0 +1,93 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/macro-n/src/app/out/IFragmentDB.gd"
+# =============================================================================
+# Author: Twister
+# Godot-IDE Extension
+#
+# Macro-N
+# =============================================================================
+
+const URI : String = "res://addons/_Godot-IDE_/plugins/macro-n/save/user_data.dat"
+
+func _setup() -> void:
+ var dir : String = URI.get_base_dir()
+ if !DirAccess.dir_exists_absolute(dir):
+ DirAccess.make_dir_absolute(dir)
+
+func _get_dto() -> Dictionary:
+ return {
+ "shortcut" : "",
+ "description" : "",
+ "content" : ""
+ }
+
+func _get_database() -> ConfigFile:
+ _setup()
+
+ var cfg : ConfigFile = ConfigFile.new()
+
+ if FileAccess.file_exists(URI) and cfg.load(URI) != OK:
+ push_error("[Macro-N] Can not restore user data saved!")
+ var dir : String = URI.get_base_dir()
+ var file : String = URI.get_file()
+ var index : int = 0
+ var uri : String = dir.path_join(str(file, "_back", index))
+ while FileAccess.file_exists(uri):
+ uri = dir.path_join(str(file, "_back", index))
+ index += 1
+ var io : FileAccess = FileAccess.open(uri, FileAccess.WRITE)
+ if io:
+ io.store_string(FileAccess.get_file_as_string(URI))
+ io.close()
+
+ return cfg
+
+#Implement IFragmentDB function.
+func save_fragment(shortcut : String, description : String, full_text : String) -> int:
+ var cfg : ConfigFile = _get_database()
+ cfg.set_value(shortcut, "description", description)
+ cfg.set_value(shortcut, "content", full_text)
+ return cfg.save(URI)
+
+#Implement IFragmentDB function.
+func get_fragment(shortcut : String) -> Dictionary:
+ var cfg : ConfigFile = _get_database()
+
+ var data : Dictionary = _get_dto()
+
+ if cfg.has_section(shortcut):
+ data["shortcut"] = shortcut
+ for k : String in data.keys():
+ if cfg.has_section_key(shortcut, k):
+ data[k] = cfg.get_value(shortcut, k, "")
+
+ return data
+
+#Implement IFragmentDB function.
+func get_all_fragments() -> Array[Dictionary]:
+ var cfg : ConfigFile = _get_database()
+ var out : Array[Dictionary] = []
+
+ for shortcut : String in cfg.get_sections():
+ var data : Dictionary = _get_dto()
+ for k : String in data.keys():
+ if cfg.has_section_key(shortcut, k):
+ data[k] = cfg.get_value(shortcut, k, "")
+
+ data["shortcut"] = shortcut
+
+ out.append(data)
+
+ return out
+
+#Implement IFragmentDB function.
+func remove(shortcut : String) -> int:
+ var db : ConfigFile = _get_database()
+ if db.has_section(shortcut):
+ db.erase_section(shortcut)
+ return db.save(URI)
+ return -1
+
+#Implement IFragmentDB function.
+func get_data_keys() -> PackedStringArray:
+ return PackedStringArray(_get_dto().keys())
diff --git a/addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd.uid b/addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd.uid
new file mode 100644
index 0000000..701232b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/macro-n/src/repo/configurator.gd.uid
@@ -0,0 +1 @@
+uid://c6pujcw1hjxo4
diff --git a/addons/_Godot-IDE_/plugins/quick_folds/plugin.cfg b/addons/_Godot-IDE_/plugins/quick_folds/plugin.cfg
new file mode 100644
index 0000000..1989eb9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/quick_folds/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="QuickFolds"
+description="Quick Folds Addon for Godot"
+author="Twister"
+version="1.0"
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/quick_folds/plugin.gd b/addons/_Godot-IDE_/plugins/quick_folds/plugin.gd
new file mode 100644
index 0000000..47166ff
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/quick_folds/plugin.gd
@@ -0,0 +1,115 @@
+@tool
+extends EditorPlugin
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Quick Folds
+# https://github.com/CodeNameTwister/Quick-Folds
+#
+# Script Spliter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const KEYS : PackedInt32Array = [
+ KEY_1,
+ KEY_2,
+ KEY_3,
+ KEY_4,
+ KEY_5,
+ KEY_6,
+ KEY_7,
+ KEY_8,
+ KEY_9,
+ KEY_0
+ ]
+
+var _inputs : Array[InputEvent] = []
+var _inverted_inputs : Array[InputEvent] = []
+
+func _init() -> void:
+ var editor : EditorSettings = EditorInterface.get_editor_settings()
+ if editor:
+ var key1 : String = "plugin/quick_folds/input/fold_type_"
+ var key2 : String = "plugin/quick_folds/input/inverted_fold_type_"
+ for z : Array in [[key1, _inputs, false], [key2, _inverted_inputs, true]]:
+ for x : int in range(0, KEYS.size(), 1):
+ var key_token : String = str(z[0], x + 1)
+ var _input : InputEvent = null
+ if editor.has_setting(key_token):
+ var variant : Variant = editor.get_setting(key_token)
+ if variant is InputEvent:
+ _input = variant
+ z[1].append(_input)
+ continue
+ _input = InputEventKey.new()
+ _input.pressed = true
+ _input.alt_pressed = true
+ _input.shift_pressed = z[2]
+ _input.keycode = KEYS[x]
+ editor.set_setting(key_token, _input)
+ z[1].append(_input)
+
+ set_process_unhandled_input(_inputs.size() > 0 or _inverted_inputs.size() > 0)
+
+func _unhandled_input(event: InputEvent) -> void:
+ if event.is_pressed():
+ for x : InputEvent in _inputs:
+ if event.is_match(x):
+ var index : int = _inputs.find(x)
+ if index > -1:
+ folding(index, false)
+ return
+ for x : InputEvent in _inverted_inputs:
+ if event.is_match(x):
+ var index : int = _inverted_inputs.find(x)
+ if index > -1:
+ folding(index, true)
+ return
+
+func _show_error(msg : String = 'Error, on try fold editor!') -> void:
+ push_warning(msg)
+
+func folding(level: int, from_back : bool) -> void:
+ var script_editor : ScriptEditor = null
+ var editor : ScriptEditorBase = null
+
+ script_editor = EditorInterface.get_script_editor()
+
+ if !is_instance_valid(script_editor):
+ _show_error()
+ return
+
+ editor = script_editor.get_current_editor()
+
+ if !is_instance_valid(editor):
+ _show_error()
+ return
+
+ var control : Control = script_editor.get_current_editor().get_base_editor()
+
+ if control is CodeEdit:
+ control.unfold_all_lines()
+
+ if from_back:
+ var max_indent : int = 0
+ for line_idx : int in range(control.get_line_count()):
+ max_indent = maxi(max_indent, control.get_indent_level(line_idx))
+
+ level = maxi(max_indent - maxi(level * control.indent_size, 0), -1)
+
+ for line : int in range(control.get_line_count()):
+ var indent: int = control.get_indent_level(line)
+ if control.can_fold_line(line):
+ if level < indent:
+ control.fold_line(line)
+ else:
+ control.unfold_line(line)
+
+ else:
+ level = maxi((level - 1) * control.indent_size, -1)
+
+ for line : int in range(control.get_line_count()):
+ var indent: int = control.get_indent_level(line)
+ if control.can_fold_line(line):
+ if level < indent:
+ control.fold_line(line)
+ else:
+ control.unfold_line(line)
diff --git a/addons/_Godot-IDE_/plugins/quick_folds/plugin.gd.uid b/addons/_Godot-IDE_/plugins/quick_folds/plugin.gd.uid
new file mode 100644
index 0000000..4751dd5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/quick_folds/plugin.gd.uid
@@ -0,0 +1 @@
+uid://bvjx2bstjy2sl
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg
new file mode 100644
index 0000000..be1c1dc
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg.import
new file mode 100644
index 0000000..b54c2b6
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://4juherhkw8hp"
+path="res://.godot/imported/Close.svg-f44a86eac1bc9e4998cafdc9b4738a96.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg"
+dest_files=["res://.godot/imported/Close.svg-f44a86eac1bc9e4998cafdc9b4738a96.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg
new file mode 100644
index 0000000..09a66e1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg
@@ -0,0 +1,39 @@
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg.import
new file mode 100644
index 0000000..977d269
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cod0nie30hnjp"
+path="res://.godot/imported/LTabBar.svg-c5ab80cb44d42fa40c9701bd78e5d30a.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg"
+dest_files=["res://.godot/imported/LTabBar.svg-c5ab80cb44d42fa40c9701bd78e5d30a.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg
new file mode 100644
index 0000000..9f28e6a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg
@@ -0,0 +1,39 @@
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg.import
new file mode 100644
index 0000000..6480f71
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://di3qxejijgp0e"
+path="res://.godot/imported/RTabBar.svg-9a0f50b61d7ac6a2c158eb70fd4d9a6f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg"
+dest_files=["res://.godot/imported/RTabBar.svg-9a0f50b61d7ac6a2c158eb70fd4d9a6f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg
new file mode 100644
index 0000000..b2af581
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg
@@ -0,0 +1,39 @@
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg.import
new file mode 100644
index 0000000..6053390
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dceedsu8mfraw"
+path="res://.godot/imported/TabBar.svg-3dd1b971b123a90fb519f5c4f78d81b1.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg"
+dest_files=["res://.godot/imported/TabBar.svg-3dd1b971b123a90fb519f5c4f78d81b1.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png b/addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png
new file mode 100644
index 0000000..4ed361e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:372e1e40caadb7b6fe6ed6a5fc66024dcf43269ac459b6adca0678220354a5cc
+size 345
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png.import
new file mode 100644
index 0000000..3d8a815
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://r6u1jtnbr4eg"
+path="res://.godot/imported/atop.png-e7eeb8db1372f6d12e66a684718d9524.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/atop.png"
+dest_files=["res://.godot/imported/atop.png-e7eeb8db1372f6d12e66a684718d9524.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg
new file mode 100644
index 0000000..c642cd7
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg.import
new file mode 100644
index 0000000..8fec137
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cractge21enk"
+path="res://.godot/imported/expand.svg-5bf3a72499998da54d96db747a1f30d1.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg"
+dest_files=["res://.godot/imported/expand.svg-5bf3a72499998da54d96db747a1f30d1.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png b/addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png
new file mode 100644
index 0000000..f5af979
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df9b95a4dceffc3419d614adf51aa908828431cef14281dd66a9c35b581a13f9
+size 2625
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png.import
new file mode 100644
index 0000000..b0f2652
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cxds5tr6aq5v3"
+path="res://.godot/imported/file_in.png-af9f02cc8f82ddae25e5704ad234b217.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png"
+dest_files=["res://.godot/imported/file_in.png-af9f02cc8f82ddae25e5704ad234b217.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg
new file mode 100644
index 0000000..2bd0014
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg
@@ -0,0 +1,55 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg.import
new file mode 100644
index 0000000..5a7a082
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cejhhnje48450"
+path="res://.godot/imported/fill_expand.svg-68507395421cbcd27a7b7775f7347f96.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg"
+dest_files=["res://.godot/imported/fill_expand.svg-68507395421cbcd27a7b7775f7347f96.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg
new file mode 100644
index 0000000..8d15944
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg
@@ -0,0 +1,199 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg.import
new file mode 100644
index 0000000..3fc6384
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b0vjxv8swip1d"
+path="res://.godot/imported/github_CodeNameTwister.svg-413c2dedf30616fd5aa27dc054f1ff13.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg"
+dest_files=["res://.godot/imported/github_CodeNameTwister.svg-413c2dedf30616fd5aa27dc054f1ff13.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg
new file mode 100644
index 0000000..b072808
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg
@@ -0,0 +1,47 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg.import
new file mode 100644
index 0000000..7f93825
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cy5kd5rqu50mo"
+path="res://.godot/imported/minus_row.svg-41677dd9d6292f7f66ac69259f155706.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg"
+dest_files=["res://.godot/imported/minus_row.svg-41677dd9d6292f7f66ac69259f155706.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg
new file mode 100644
index 0000000..010cf26
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg
@@ -0,0 +1,51 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg.import
new file mode 100644
index 0000000..3fddf5e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://7rel5pr2g7d2"
+path="res://.godot/imported/pin.svg-178508cb55a2e5067fd62293946886b7.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg"
+dest_files=["res://.godot/imported/pin.svg-178508cb55a2e5067fd62293946886b7.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg
new file mode 100644
index 0000000..c5e6934
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg
@@ -0,0 +1,41 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg.import
new file mode 100644
index 0000000..469ffad
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cj6sg45m64fiv"
+path="res://.godot/imported/plus_row.svg-947a3d1b90cc7713424371beaa7e9f65.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg"
+dest_files=["res://.godot/imported/plus_row.svg-947a3d1b90cc7713424371beaa7e9f65.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg
new file mode 100644
index 0000000..8981386
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg.import
new file mode 100644
index 0000000..dff45a7
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c1b7aoplxr0x7"
+path="res://.godot/imported/split_cminus.svg-e2809c97c9a0d3913d3b9355b3d51b1d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg"
+dest_files=["res://.godot/imported/split_cminus.svg-e2809c97c9a0d3913d3b9355b3d51b1d.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg
new file mode 100644
index 0000000..9c7b077
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg
@@ -0,0 +1,81 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg.import
new file mode 100644
index 0000000..75426e7
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d3s0q8570ppjv"
+path="res://.godot/imported/split_cminus_tool.svg-ef687c17cf4f3e2eb6c96350a9b11a99.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg"
+dest_files=["res://.godot/imported/split_cminus_tool.svg-ef687c17cf4f3e2eb6c96350a9b11a99.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg
new file mode 100644
index 0000000..2cc4a56
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg.import
new file mode 100644
index 0000000..ea56119
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bvyjqo1dus1xu"
+path="res://.godot/imported/split_cplus.svg-9fdcb19f5d8e4547181323a8dddac482.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg"
+dest_files=["res://.godot/imported/split_cplus.svg-9fdcb19f5d8e4547181323a8dddac482.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg
new file mode 100644
index 0000000..4e76e51
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg
@@ -0,0 +1,90 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg.import
new file mode 100644
index 0000000..7e6bd01
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://6pq6vesk5ycg"
+path="res://.godot/imported/split_cplus_tool.svg-4d052e790c616fae56d146b3e9b403a7.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg"
+dest_files=["res://.godot/imported/split_cplus_tool.svg-4d052e790c616fae56d146b3e9b403a7.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg
new file mode 100644
index 0000000..68ee0b2
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg
@@ -0,0 +1,68 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg.import
new file mode 100644
index 0000000..4a84207
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://btm5y6ifmbles"
+path="res://.godot/imported/split_minus_tool.svg-69be4b426393a3ce9ec2cf8029858538.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg"
+dest_files=["res://.godot/imported/split_minus_tool.svg-69be4b426393a3ce9ec2cf8029858538.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg
new file mode 100644
index 0000000..82ae148
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg.import
new file mode 100644
index 0000000..a08dd34
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bg17j5ifvqsi8"
+path="res://.godot/imported/split_plus_tool.svg-70364f620a127c87706f04c3c5713c37.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg"
+dest_files=["res://.godot/imported/split_plus_tool.svg-70364f620a127c87706f04c3c5713c37.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg
new file mode 100644
index 0000000..50fa1a2
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg.import
new file mode 100644
index 0000000..c451c54
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bvjk4po8w2m2j"
+path="res://.godot/imported/split_rminus.svg-1ef0b3f2a9114007898d1e4c47d28a4f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg"
+dest_files=["res://.godot/imported/split_rminus.svg-1ef0b3f2a9114007898d1e4c47d28a4f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg
new file mode 100644
index 0000000..471eb43
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg
@@ -0,0 +1,81 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg.import
new file mode 100644
index 0000000..262a417
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d1yp7jd6hvabl"
+path="res://.godot/imported/split_rminus_tool.svg-7e3379363d3a251c15b40f135e3f5132.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg"
+dest_files=["res://.godot/imported/split_rminus_tool.svg-7e3379363d3a251c15b40f135e3f5132.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg
new file mode 100644
index 0000000..81e2b3a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg
@@ -0,0 +1,92 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg.import
new file mode 100644
index 0000000..c7eabd8
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://1sqajw0mikdj"
+path="res://.godot/imported/split_rplus.svg-a38a9e7660a8f7d4f7e55dc2aa40dce2.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg"
+dest_files=["res://.godot/imported/split_rplus.svg-a38a9e7660a8f7d4f7e55dc2aa40dce2.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg
new file mode 100644
index 0000000..7e7967d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg
@@ -0,0 +1,90 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg.import
new file mode 100644
index 0000000..93f5647
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://den018oksgm5m"
+path="res://.godot/imported/split_rplus_tool.svg-a22d42ef806d4f5441bc1950a87a36c6.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg"
+dest_files=["res://.godot/imported/split_rplus_tool.svg-a22d42ef806d4f5441bc1950a87a36c6.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd
new file mode 100644
index 0000000..2bc9efa
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd
@@ -0,0 +1,201 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+const Builder = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd")
+const Context = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd")
+const SSPContext = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd")
+
+var _plugin : EditorPlugin = null
+var _builder : Builder = null
+
+signal add_row(value : Resource)
+signal add_column(value : Resource)
+signal remove_row(value : Resource)
+signal remove_column(value : Resource)
+
+signal left_tab_close(value : Resource)
+signal right_tab_close(value : Resource)
+signal others_tab_close(value : Resource)
+
+const ICON_ADD_COLUMN : Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus.svg")
+const ICON_ADD_ROW : Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus.svg")
+const ICON_REMOVE_COLUMN : Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus.svg")
+const ICON_REMOVE_ROW : Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus.svg")
+
+const L_TAB_BAR : Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/LTabBar.svg")
+const R_TAB_BAR : Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/RTabBar.svg")
+const TAB_BAR: Texture2D = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/TabBar.svg")
+
+
+var _context_add_split_column : Context = null
+var _context_add_split_row : Context = null
+var _context_remove_split_column : Context = null
+var _context_remove_split_row : Context = null
+var _context_editor_split : SSPContext = null
+
+var _editor_context_add_split_column : Context = null
+var _editor_context_add_split_row : Context = null
+var _editor_context_remove_split_column : Context = null
+var _editor_context_remove_split_row : Context = null
+
+var _editor_context_left_tab_close : Context = null
+var _editor_context_right_tab_close : Context = null
+var _editor_context_botH_tab_close : Context = null
+
+func get_honey_splitter() -> SSPContext:
+ return _context_editor_split
+
+# Traduction?
+func _tr(message : String) -> String:
+ # ...
+ return message.capitalize()
+
+func init_1() -> void:
+
+ _context_add_split_column = Context.new(_tr("SPLIT_COLUMN"), _add_column_split, _can_split, ICON_ADD_COLUMN)
+ _context_add_split_row = Context.new(_tr("SPLIT_ROW"), _add_row_split, _can_split, ICON_ADD_ROW)
+ _context_remove_split_column = Context.new(_tr("MERGE_SPLITTED_COLUMN"), _remove_column_split, _can_merge_column, ICON_REMOVE_COLUMN)
+ _context_remove_split_row = Context.new(_tr("MERGE_SPLITTED_ROW"), _remove_row_split, _can_merge_row, ICON_REMOVE_ROW)
+ _context_editor_split = SSPContext.new()
+
+ _editor_context_add_split_column = Context.new(_tr("SPLIT_COLUMN"), _add_column_split, _can_split, ICON_ADD_COLUMN)
+ _editor_context_add_split_row = Context.new(_tr("SPLIT_ROW"), _add_row_split, _can_split, ICON_ADD_ROW)
+ _editor_context_remove_split_column = Context.new(_tr("MERGE_SPLITTED_COLUMN"), _remove_column_split, _can_merge_column, ICON_REMOVE_COLUMN)
+ _editor_context_remove_split_row = Context.new(_tr("MERGE_SPLITTED_ROW"), _remove_row_split, _can_merge_row, ICON_REMOVE_ROW)
+
+ _editor_context_left_tab_close = Context.new(_tr("CLOSE_LEFT_TABS"), _left_tab_close, _can_left_tab_close, L_TAB_BAR)
+ _editor_context_botH_tab_close = Context.new(_tr("CLOSE_OTHERS_TABS"), _others_tab_close, _can_others_tab_close, TAB_BAR)
+ _editor_context_right_tab_close = Context.new(_tr("CLOSE_RIGHT_TABS"), _right_tab_close, _can_right_tab_close, R_TAB_BAR)
+
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _context_add_split_column)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _context_add_split_row)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _context_remove_split_column)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _context_remove_split_row)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _context_editor_split)
+
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_add_split_column)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_add_split_row)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_remove_split_column)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_remove_split_row)
+
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_left_tab_close)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_right_tab_close)
+ _plugin.add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_botH_tab_close)
+
+
+
+func _get_value(value : Variant) -> PackedStringArray:
+ if value is PackedStringArray:
+ return value
+
+ elif value is Array:
+ var packed : PackedStringArray = []
+ for x : Variant in value:
+ if x is Resource:
+ packed.append(x.resource_path)
+ return packed
+ elif x is String:
+ packed.append(x)
+ return packed
+
+ elif value is Resource:
+ var packed : PackedStringArray = [value.resource_path]
+ return packed
+ return []
+
+func _get_resource(value : Variant) -> Variant:
+ if value is Resource:
+ return value
+ elif value is Node:
+ return value
+
+ var packed : PackedStringArray = []
+ if value is Array:
+ for x : Variant in value:
+ if x is String:
+ packed.append(x)
+ break
+ elif value is PackedStringArray:
+ packed = value
+
+ if packed.size() == 0:
+ return null
+
+ return packed[0]
+
+func _can_split(value : Variant = null) -> bool:
+ return _plugin.builder.can_split(_get_value(value))
+
+func _can_merge_column(value : Variant = null) -> bool:
+ return _plugin.builder.can_merge_column(_get_value(value))
+
+func _can_merge_row(value : Variant = null) -> bool:
+ return _plugin.builder.can_merge_row(_get_value(value))
+
+func _can_left_tab_close(value : Variant = null) -> bool:
+ return _plugin.builder.can_left_tab_close(_get_value(value))
+
+func _can_right_tab_close(value : Variant = null) -> bool:
+ return _plugin.builder.can_right_tab_close(_get_value(value))
+
+func _can_others_tab_close(value : Variant = null) -> bool:
+ return _plugin.builder.can_others_tab_close(_get_value(value))
+
+func _left_tab_close(value : Variant = null) -> void:
+ left_tab_close.emit(_get_resource(value))
+
+func _right_tab_close(value : Variant = null) -> void:
+ right_tab_close.emit(_get_resource(value))
+
+func _others_tab_close(value : Variant = null) -> void:
+ others_tab_close.emit(_get_resource(value))
+
+func _add_column_split(value : Variant = null) -> void:
+ add_column.emit(_get_resource(value))
+
+func _add_row_split(value : Variant = null) -> void:
+ add_row.emit(_get_resource(value))
+
+func _remove_column_split(value : Variant = null) -> void:
+ remove_column.emit(_get_resource(value))
+
+func _remove_row_split(value : Variant = null) -> void:
+ remove_row.emit(_get_resource(value))
+
+func init_0() -> void:
+ for x : Variant in [
+ _context_add_split_column,
+ _context_add_split_row,
+ _context_remove_split_column,
+ _context_remove_split_row,
+ _context_editor_split,
+ _editor_context_add_split_column,
+ _editor_context_add_split_row,
+ _editor_context_remove_split_column,
+ _editor_context_remove_split_row
+ ]:
+ if is_instance_valid(x):
+ _plugin.remove_context_menu_plugin(x)
+
+func _init(plugin : EditorPlugin, builder : Builder) -> void:
+ _plugin = plugin
+ _builder = builder
+
+func event(event : InputEvent) -> bool:
+ if event.is_pressed():
+ if event is InputEventKey:
+ if event.keycode == KEY_1 and event.ctrl_pressed:
+ _plugin.builder.multi_split(2, false)
+ pass
+ if event.keycode == KEY_2 and event.ctrl_pressed:
+ _plugin.builder.multi_split(4, false)
+ pass
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd.uid
new file mode 100644
index 0000000..b39dcb4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd.uid
@@ -0,0 +1 @@
+uid://dxipxeq42djlp
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd
new file mode 100644
index 0000000..f9116f6
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd
@@ -0,0 +1,197 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const SplitterContainer = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd")
+const NControl = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd")
+
+const IoBar = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd")
+
+signal update()
+signal focus_by_tab(root : TabContainer, index : int)
+signal remove_by_tab(root : TabContainer, index : int)
+signal change_container(container : TabContainer)
+signal exiting()
+
+@warning_ignore("unused_signal")
+signal rmb_click(index : int, TabContainer)
+
+@warning_ignore("unused_signal")
+signal swap_tab(from : Container, index : int, to : Container)
+
+var _editor_container : TabContainer = null
+var _editor_splitter_container : SplitterContainer = null
+
+var _current_container : TabContainer = null:
+ set(e):
+ if _current_container != e:
+ change_container.emit(e)
+ _current_container = e
+
+var _frm : int = 0
+
+var _io_bar : Node = null
+
+func on_focus(root : TabContainer, index : int) -> void:
+ focus_by_tab.emit(root, index)
+
+func on_remove(root : TabContainer, index : int) -> void:
+ remove_by_tab.emit(root, index)
+
+func get_io_bar() -> IoBar:
+ if !is_instance_valid(_io_bar):
+ _io_bar = IoBar.new()
+ return _io_bar
+
+func get_container(control : Control) -> Container:
+ if control is SplitterContainer.SplitterEditorContainer.Editor:
+ return _editor_splitter_container.get_base_container(control)
+ return null
+
+func get_container_item(control : Control) -> Control:
+ if control is SplitterContainer.SplitterEditorContainer.Editor:
+ return _editor_splitter_container.get_base_container_item(control)
+ return null
+
+func _init(container : TabContainer) -> void:
+ _editor_container = container
+ _editor_splitter_container = SplitterContainer.new()
+ _editor_splitter_container.initialize(_editor_container, self)
+ _editor_splitter_container.visible = false
+
+ _editor_container.child_entered_tree.connect(_on_update)
+ _editor_container.child_exiting_tree.connect(_on_update)
+
+ _editor_container.tree_exiting.connect(_on_exiting)
+
+
+func is_active() -> bool:
+ if _frm > 0:
+ _frm -= 1
+ return false
+ return is_instance_valid(_editor_container) and _editor_container.is_inside_tree()
+
+func _on_exiting() -> void:
+ _frm = 3
+ exiting.emit()
+
+func initialize_editor_container() -> void:
+ _editor_splitter_container.initialize_editor_contianer()
+
+func _on_update(__ : Node) -> void:
+ update.emit()
+
+func set_current_container(container : TabContainer) -> void:
+ if _editor_splitter_container.set_current_editor(container):
+ _current_container = container
+
+func get_editor_container() -> TabContainer:
+ return _editor_container
+
+func get_root_container() -> SplitterContainer.SplitterRoot:
+ return _editor_splitter_container.get_root()
+
+func get_editor_root_container(node : Node) -> SplitterContainer.BaseContainerItem:
+ if node is SplitterContainer.SplitterRoot:
+ node = node.get_parent()
+ return node
+ return null
+
+func get_editors() -> Array[Node]:
+ return _editor_container.get_children()
+
+func get_current_editor() -> Control:
+ return _editor_splitter_container.get_current_editor()
+
+func tool_created() -> void:
+ _editor_container.visible = false
+ _editor_splitter_container.visible = true
+
+func new_column() -> Control:
+ _current_container = _editor_splitter_container.create_new_column()
+ return _current_container
+
+func new_row() -> Control:
+ _current_container = _editor_splitter_container.create_new_row()
+ return _current_container
+
+func update_split_container() -> void:
+ for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__ST_CS__"):
+ if x.has_method(&"update"):
+ x.call(&"update")
+
+func get_all_containers() -> Array[Node]:
+ if !_editor_splitter_container:
+ return []
+ return _editor_splitter_container.get_tree().get_nodes_in_group(&"__SP_BR__")
+
+func get_current_containers() -> Array[Node]:
+ if !is_instance_valid(_current_container):
+ return []
+ var c : Control = _editor_splitter_container.get_base_container(_current_container)
+ if is_instance_valid(c):
+ return c.get_children()
+ return []
+
+func get_all_splitters() -> Array[Node]:
+ if !_editor_splitter_container:
+ return []
+ return _editor_splitter_container.get_tree().get_nodes_in_group(&"__SC_SPLITTER__")
+
+func get_current_splitters() -> Array[Node]:
+ if !is_instance_valid(_current_container):
+ return []
+ var c : Control = _editor_splitter_container.get_base_container_item(_current_container)
+ if is_instance_valid(c):
+ c = c.get_parent()
+ if c:
+ return c.get_children()
+ return []
+
+func garbage() -> void:
+ var control : Node = get_current_editor()
+
+ var nodes : Array[Node] = get_all_splitters()
+ var total : int = nodes.size()
+ if total > 2:
+ total = 0
+ for x : Node in nodes:
+ if !x.is_queued_for_deletion():
+ total += 1
+
+ if total > 1:
+ for x : Node in nodes:
+ if total < 2:
+ break
+ if x.get_child_count() == 0:
+ if control == x:
+ control = null
+ if !x.is_queued_for_deletion():
+ x.queue_free()
+ total -= 1
+
+ if control == null:
+ for x : Node in _editor_splitter_container.get_tree().get_nodes_in_group(&"__SC_SPLITTER__"):
+ if x is Control and !x.is_queued_for_deletion():
+ control = x
+ break
+
+func reset() -> void:
+ _editor_container.visible = true
+
+ if _editor_container.child_entered_tree.is_connected(_on_update):
+ _editor_container.child_entered_tree.disconnect(_on_update)
+ if _editor_container.child_exiting_tree.is_connected(_on_update):
+ _editor_container.child_exiting_tree.disconnect(_on_update)
+
+ _editor_splitter_container.reset()
+ _editor_splitter_container.queue_free()
+
+func get_current_container() -> TabContainer:
+ return _current_container
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd.uid
new file mode 100644
index 0000000..a26a995
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd.uid
@@ -0,0 +1 @@
+uid://iyslc58y0lp1
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd
new file mode 100644
index 0000000..0b4cfa8
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd
@@ -0,0 +1,277 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const SplitterList = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd")
+
+signal item_selected(item : int)
+signal updated()
+
+var _editor_list : ItemList = null
+var _script_list : ItemList = null
+var _script_filesearch : LineEdit = null
+var _editor_filesearch : LineEdit = null
+var _update_list_queue : bool = false
+var _array_list : Array = []
+var _selet_queue : int = -1
+var _selecting : bool = false
+
+var update_selections_callback : Callable
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ if is_instance_valid(_editor_list):
+ _editor_list.visible = true
+ if _editor_list.item_selected.is_connected(_item_selected):
+ _editor_list.item_selected.disconnect(_item_selected)
+ if _editor_list.property_list_changed.is_connected(_on_property):
+ _editor_list.property_list_changed.disconnect(_on_property)
+ if is_instance_valid(_editor_filesearch):
+ _editor_filesearch.visible = true
+
+ if is_instance_valid(_script_filesearch):
+ _script_filesearch.queue_free()
+
+ if is_instance_valid(_script_list):
+ _script_list.queue_free()
+
+func _on_sc_item_selected(index : int) -> void:
+ if _script_list.item_count > index and index > -1:
+ index = _get_script_selected(index)
+ if index == -1:
+ return
+ select(index)
+
+func _on_sc_item_activate(index : int) -> void:
+ if _script_list.item_count > index:
+ index = _get_script_selected(index)
+ if index > -1 and index < _editor_list.item_count:
+ _editor_list.item_activated.emit(index)
+
+func _on_property() -> void:
+ _script_list.update()
+
+func _on_sc_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void:
+ if _script_list.item_count > index:
+ index = _get_script_selected(index)
+ if index == -1:
+ return
+ _editor_list.item_clicked.emit(index, at_position, mouse_button_index)
+ _script_list.update()
+
+func _get_script_selected(index : int) -> int:
+ if _editor_list.item_count == _script_list.item_count:
+ return index
+
+ var tp : String = _script_list.get_item_tooltip(index)
+ var cindx : int = -1
+ if !tp.is_empty():
+ for x : int in _editor_list.item_count:
+ if tp == _editor_list.get_item_tooltip(x):
+ cindx = x
+ break
+ else:
+ tp = _script_list.get_item_text(index)
+ for x : int in _editor_list.item_count:
+ if tp == _editor_list.get_item_text(x):
+ cindx = x
+ break
+
+ return cindx
+
+#func set_handler(manager : Object) -> void:
+ #_script_list.set_handler(manager)
+ #
+func _init(list : ItemList) -> void:
+ _editor_list = list
+ _editor_list.item_selected.connect(_item_selected)
+ _editor_list.property_list_changed.connect(_on_property)
+
+ var parent: Node = _editor_list.get_parent()
+ _script_list = list.duplicate()
+ _script_list.set_script(SplitterList)
+ _script_list.set_reference(_update_list)
+ _script_list.set_list(_editor_list)
+ _script_list.item_selected.connect(_on_sc_item_selected)
+ _script_list.item_activated.connect(_on_sc_item_activate)
+ _script_list.item_clicked.connect(_on_sc_item_clicked)
+ #_editor_list.draw.connect(_on_update_list)
+
+ _script_list.add_to_group(&"__SP_LT__")
+ _array_list = [_editor_list, _script_list]
+
+ list.visible = false
+
+ var filesearch : Object = parent.get_child(0)
+ if filesearch is LineEdit:
+ _editor_filesearch = filesearch
+ var txt : String = filesearch.text
+ if !txt.is_empty():
+ filesearch.set(&"text", "")
+
+ _script_filesearch = filesearch.duplicate()
+ _script_filesearch.text_changed.connect(_on_update_list_search)
+
+ filesearch.visible = false
+
+ parent.add_child(_script_list)
+ parent.move_child(_script_list, 0)
+ parent.add_child(_script_filesearch)
+ parent.move_child(_script_filesearch, 0)
+
+ _script_list.update()
+
+func _on_update_list() -> void:
+ if _update_list_queue:
+ return
+
+ if !is_instance_valid(_script_list) or !is_instance_valid(_editor_list):
+ return
+
+ _update_list_queue = true
+
+ var filtered : bool = false
+
+ if is_instance_valid(_script_filesearch):
+ filtered = !_script_filesearch.text.is_empty()
+
+
+ var item_list : ItemList = _editor_list
+
+ _script_list.clear()
+
+ if filtered:
+ _on_update_list_search(_script_filesearch.text)
+ else:
+ for x : int in item_list.item_count:
+ var indx : int = _script_list.item_count
+ _script_list.add_item(item_list.get_item_text(x), item_list.get_item_icon(x), true)
+ _script_list.set_item_metadata(indx, item_list.get_item_metadata(x))
+ _script_list.set_item_tooltip(indx, item_list.get_item_tooltip(x))
+ _script_list.set_item_icon_modulate(indx, item_list.get_item_icon_modulate(x))
+ _script_list.set_item_custom_fg_color(indx, item_list.get_item_custom_fg_color(x))
+
+ update_list_selection()
+
+ set_deferred(&"_update_list_queue", false)
+
+func _on_update_list_search(txt : String) -> void:
+ if txt.is_empty():
+ _on_update_list()
+ return
+
+ if !is_instance_valid(_script_list):
+ return
+
+ _script_list.clear()
+
+ var rgx : RegEx = RegEx.create_from_string("(?i).*{0}.*".format([txt]))
+
+ if !is_instance_valid(rgx) or !rgx.is_valid():
+ return
+
+ var item_list : ItemList = _editor_list
+ for x : int in item_list.item_count:
+ var _txt : String = item_list.get_item_text(x)
+ if rgx.search(_txt) != null:
+ var indx : int = _script_list.add_item(item_list.get_item_text(x), item_list.get_item_icon(x), true)
+ _script_list.set_item_metadata(indx, item_list.get_item_metadata(x))
+ _script_list.set_item_tooltip(indx, item_list.get_item_tooltip(x))
+ _script_list.set_item_icon_modulate(indx, item_list.get_item_icon_modulate(x))
+ _script_list.set_item_custom_fg_color(indx, item_list.get_item_custom_fg_color(x))
+
+ update_list_selection()
+
+func update_list_selection() -> void:
+ if update_selections_callback.is_valid():
+ update_selections_callback.call(_array_list)
+
+func _item_selected(i : int) -> void:
+ item_selected.emit(i)
+
+func _update_list() -> void:
+ updated.emit()
+ _on_update_list()
+
+func get_editor_list() -> ItemList:
+ return _editor_list
+
+func get_selected_id() -> int:
+ for x : int in range(_editor_list.item_count):
+ if _editor_list.is_selected(x):
+ return x
+ return -1
+
+func remove(index : int) -> void:
+ if _editor_list.item_count > index and index > -1:
+ _editor_list.item_clicked.emit(index, _editor_list.get_local_mouse_position(), MOUSE_BUTTON_MIDDLE)
+
+func item_count() -> int:
+ return _editor_list.item_count
+
+func _select() -> void:
+ if _selet_queue > -1 and _editor_list.item_count > _selet_queue:
+ _editor_list.select(_selet_queue, true)
+ _editor_list.item_selected.emit(_selet_queue)
+ _update_list.call_deferred()
+ _selecting = false
+
+func update_list() -> void:
+ _on_update_list()
+
+func select(i : int) -> void:
+ if i > -1 and _editor_list.item_count > i:
+ _selet_queue = i
+ if _selecting:
+ return
+ _selecting = true
+ _select.call_deferred()
+
+func is_selected(i : int) -> bool:
+ if _editor_list.item_count > i and i > -1:
+ return _editor_list.is_selected(i)
+ return false
+
+func get_item_tooltip(item : int) -> String:
+ if _editor_list.item_count > item and item > -1:
+ return _editor_list.get_item_tooltip(item)
+ return ""
+
+func get_item_icon(item : int) -> Texture2D:
+ if _editor_list.item_count > item and item > -1:
+ return _editor_list.get_item_icon(item)
+ return null
+
+func get_item_icon_modulate(item : int) -> Color:
+ if _editor_list.item_count > item and item > -1:
+ return _editor_list.get_item_icon_modulate(item)
+ return Color.WHITE
+
+func get_item_text(item : int) -> String:
+ if _editor_list.item_count > item and item > -1:
+ return _editor_list.get_item_text(item)
+ return ""
+
+func reset() -> void:
+ if is_instance_valid(_editor_list):
+ _editor_list.visible = true
+ if _editor_list.draw.is_connected(_on_update_list):
+ _editor_list.draw.disconnect(_on_update_list)
+ if _editor_list.item_selected.is_connected(_item_selected):
+ _editor_list.item_selected.disconnect(_item_selected)
+ if _editor_list.property_list_changed.is_connected(_on_property):
+ _editor_list.property_list_changed.disconnect(_on_property)
+
+ if is_instance_valid(_editor_filesearch):
+ _editor_filesearch.visible = true
+
+ if is_instance_valid(_script_filesearch):
+ _script_filesearch.queue_free()
+
+ if is_instance_valid(_script_list):
+ _script_list.queue_free()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd.uid
new file mode 100644
index 0000000..2ffa5c2
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd.uid
@@ -0,0 +1 @@
+uid://ors5ojuayup4
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd
new file mode 100644
index 0000000..8d14728
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd
@@ -0,0 +1,242 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const EditorManager = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd")
+const BaseContainer = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd")
+const BaseList = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd")
+
+var _plugin : EditorPlugin = null
+var _editor_manager : EditorManager = null
+
+
+#region _REF_
+var _item_list : ItemList = null:
+ get:#
+ if !is_instance_valid(_item_list):
+ var script_editor: ScriptEditor = EditorInterface.get_script_editor()
+ var items : Array[Node] = script_editor.find_children("*", "ItemList", true, false)
+ if items.size() > 0:
+ _item_list = items[0]
+ else:
+ push_warning("[Script-Splitter] Can not find item list!")
+ return _item_list
+#endregion
+
+func handle(id : StringName) -> void:
+ _editor_manager.io.execute(id)
+
+func refresh_warnings() -> void:
+ _editor_manager.refresh_warnings.execute()
+
+func can_split(values : Variant) -> bool:
+ var current : Node = null
+ if values is PackedStringArray and values.size() > 0:
+ var root : Node = _plugin.get_tree().root
+ if root.has_node(values[0]):
+ current = root.get_node(values[0])
+ elif values is Node:
+ current = values
+ return _editor_manager.get_current_totaL_editors(current) > 1
+
+func can_merge_column(values : Variant) -> bool:
+ var current : Node = null
+ if values is PackedStringArray and values.size() > 0:
+ var root : Node = _plugin.get_tree().root
+ if root.has_node(values[0]):
+ current = root.get_node(values[0])
+ elif values is Node:
+ current = values
+ return _editor_manager.get_current_total_splitters(current) > 1
+
+func can_merge_row(_values : Variant) -> bool:
+ return _editor_manager.get_total_split_container(true) > 1
+
+func can_left_tab_close(values : Variant) -> bool:
+ if values is PackedStringArray and values.size() > 0:
+ var root : Node = _plugin.get_tree().root
+ if root.has_node(values[0]):
+ values = root.get_node(values[0])
+ else:
+ values = values[0]
+ var node : Node = _editor_manager.get_control_tool_by_current(values)
+ return node and node.get_index() > 0
+
+func can_right_tab_close(values : Variant) -> bool:
+ if values is PackedStringArray and values.size() > 0:
+ var root : Node = _plugin.get_tree().root
+ if root.has_node(values[0]):
+ values = root.get_node(values[0])
+ else:
+ values = values[0]
+ var node : Node = _editor_manager.get_control_tool_by_current(values)
+ return node and node.get_index() < node.get_parent().get_child_count() - 1
+
+func can_others_tab_close(values : Variant) -> bool:
+ return can_left_tab_close(values) and can_right_tab_close(values)
+
+func update(_delta : float) -> void:
+ if _editor_manager.update():
+ _plugin.set_process(false)
+
+func multi_split(number : int, as_row : bool) -> void:
+ var total : int = _editor_manager.get_current_total_splitters(null)
+ if total == number:
+ return
+ var container : Node = _editor_manager.get_current_root()
+ if !as_row:
+ if total < number:
+ number = number - total
+ while number > 0:
+ if !can_split(container):
+ return
+ _editor_manager.split_column.execute(container)
+ number -= 1
+ else:
+ number = total - number
+ while number > 0:
+ if !can_merge_column(container):
+ return
+ _editor_manager.merge_tool.execute([_editor_manager.get_current_tool(container), false])
+ number -= 1
+ if !as_row:
+ if total < number:
+ number = number - total
+ while number > 0:
+ if !can_split(container):
+ return
+ _editor_manager.split_row.execute(container)
+ number -= 1
+ else:
+ number = total - number
+ while number > 0:
+ if !can_merge_column(container):
+ return
+ _editor_manager.merge_tool.execute([_editor_manager.get_current_tool(container), true])
+ number -= 1
+
+func init_0() -> void:
+ if is_instance_valid(_editor_manager):
+ _editor_manager.reset()
+ _editor_manager = null
+
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ if editor.editor_script_changed.is_connected(_on_change):
+ editor.editor_script_changed.disconnect(_on_change)
+
+func _on_change(__ : Variant = null) -> void:
+ _queue_update()
+
+func connect_callbacks(
+ on_column : Signal,
+ on_row : Signal,
+ out_column : Signal,
+ out_row : Signal,
+ left_tab_close : Signal,
+ right_tab_close : Signal,
+ others_tab_close : Signal,
+
+ do_connect : bool = true) -> void:
+ for x : Array in [
+ [on_column, _editor_manager.split_column.execute],
+ [on_row, _editor_manager.split_row.execute],
+ [out_column, _editor_manager.unsplit_column],
+ [out_row, _editor_manager.unsplit_row],
+ [left_tab_close, _editor_manager.left_tab_close],
+ [right_tab_close, _editor_manager.right_tab_close],
+ [others_tab_close, _editor_manager.others_tab_close]
+ ]:
+ if !x[0].is_null():
+ if do_connect:
+ if !x[0].is_connected(x[1]):
+ x[0].connect(x[1])
+ else:
+ if x[0].is_connected(x[1]):
+ x[0].disconnect(x[1])
+func _nws() -> void:
+ print("[Script Splitter] New Splitter System!\nNow use controls in toolbar for split columns and rows as you like!\nPlease provide feedback on the Github issues tab [https://github.com/CodeNameTwister/Script-Splitter]")
+
+func swap_by_src(from : String, to : String, as_left : bool) -> void:
+ _editor_manager.swap_tab.execute([from, to, as_left])
+
+func _clean_settings() -> void:
+ var e : EditorSettings = EditorInterface.get_editor_settings()
+ if e.has_setting("plugin/script_spliter/rows"):
+ _nws()
+ e.set_setting("plugin/script_spliter/rows", null)
+ e.set_setting("plugin/script_spliter/columns", null)
+ e.set_setting("plugin/script_spliter/save_rows_columns_count_on_exit", null)
+ e.set_setting("plugin/script_spliter/window/use_highlight_selected", null)
+ e.set_setting("plugin/script_spliter/window/highlight_selected_color", null)
+ e.set_setting("plugin/script_spliter/editor/split/reopen_last_closed_editor_on_add_split", null)
+ e.set_setting("plugin/script_spliter/editor/split/remember_last_used_editor_buffer_size", null)
+ e.set_setting("plugin/script_spliter/behavior/auto_create_split_by_config", null)
+ e.set_setting("plugin/script_spliter/editor/list/colorize_actives", null)
+
+ for x : String in [
+ "plugin/script_spliter/behaviour/refresh_warnings_on_save"
+ ,"plugin/script_spliter/editor/out_focus_color_value"
+ ,"plugin/script_spliter/editor/out_focus_color_enabled"
+ ,"plugin/script_spliter/editor/minimap_for_unfocus_window"
+ ,"plugin/script_spliter/editor/behaviour/expand_on_focus"
+ ,"plugin/script_spliter/editor/behaviour/can_expand_on_same_focus"
+ ,"plugin/script_spliter/editor/behaviour/smooth_expand"
+ ,"plugin/script_spliter/editor/behaviour/smooth_expand_time"
+ ,"plugin/script_spliter/editor/behaviour/swap_by_double_click_separator_button"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/handle_back_and_forward"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/history_size"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/using_as_next_and_back_tab"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/use_native_handler_when_there_are_no_more_tabs"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/backward_key_button_input"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/forward_key_button_input"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/backward_mouse_button_input"
+ ,"plugin/script_spliter/editor/behaviour/back_and_forward/forward_mouse_button_input"
+ ,"plugin/script_spliter/editor/list/selected_color"
+ ,"plugin/script_spliter/editor/list/others_color"
+ ,"plugin/script_spliter/editor/tabs/use_old_behaviour"
+ ,"plugin/script_spliter/line/size"
+ ,"plugin/script_spliter/line/color"
+ ,"plugin/script_spliter/line/draggable"
+ ,"plugin/script_spliter/line/expand_by_double_click"
+ ,"plugin/script_spliter/line/button/size"
+ ,"plugin/script_spliter/line/button/modulate"
+ ,"plugin/script_spliter/behavior/create_all_open_editors"
+ ]:
+
+ if e.has_setting(x):
+ e.set_setting(x.replace("/script_spliter/", "/script_splitter/"), e.get_setting(x))
+ e.set_setting(x, null)
+
+ for x : int in range(1, 11, 1):
+ e.set_setting(str("plugin/script_spliter/input/split_type_" , x), null)
+
+ #for x : int in range(1, 11, 1):
+ #e.set_setting(str("plugin/script_splitter/input/split_type_" , x), null)
+
+
+func init_1(plugin : EditorPlugin, tab_container : TabContainer, item_list : ItemList) -> void:
+ if !is_instance_valid(plugin) or !is_instance_valid(tab_container):
+ printerr("Error, can`t initalize plugin, not valid references!")
+ return
+
+ _clean_settings()
+ _plugin = plugin
+ _plugin.set_process(true)
+
+ _editor_manager = EditorManager.new(BaseContainer.new(tab_container), BaseList.new(item_list))
+ _editor_manager.update_request.connect(_queue_update)
+
+ var editor : ScriptEditor = EditorInterface.get_script_editor()
+ if editor:
+ if !editor.editor_script_changed.is_connected(_on_change):
+ editor.editor_script_changed.connect(_on_change)
+
+func _queue_update() -> void:
+ _plugin.set_process(true)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd.uid
new file mode 100644
index 0000000..303723d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd.uid
@@ -0,0 +1 @@
+uid://dge4wucvh6qnb
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd
new file mode 100644
index 0000000..0ed6eef
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd
@@ -0,0 +1,264 @@
+@tool
+extends EditorContextMenuPlugin
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4f
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const PLUS_SPLIT = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/plus_row.svg")
+const MINUS_SPLIT = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/minus_row.svg")
+const SspEditor = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd")
+
+var _vsplits : Array[VSplitContainer] = []
+
+func _translate(_str : String) -> String:
+ # ...
+ return _str
+
+func _popup_menu(_paths : PackedStringArray) -> void:
+ var sc : ScriptEditor = EditorInterface.get_script_editor()
+
+ if !is_instance_valid(sc.get_current_script()):
+ return
+
+ var ed : ScriptEditorBase = sc.get_current_editor()
+ var be : Control = ed.get_base_editor()
+
+
+ if be is CodeEdit:
+ if !(be.get_parent() is VSplitContainer):
+ add_context_menu_item(_translate("Sub-Split"), _on_sub_split, PLUS_SPLIT)
+ else:
+ add_context_menu_item(_translate("Remove Sub-Split"), _out_sub_split, MINUS_SPLIT)
+
+func is_handled(cnt : Node) -> bool:
+ return cnt is CodeEdit and cnt.get_parent() is VSplitContainer
+
+
+func split() -> void:
+ _on_sub_split(null)
+
+func merge(value : Node) -> void:
+ _out_sub_split(value)
+
+func _out_sub_split(value : Variant = null) -> void:
+ var be : Control = null
+ if value is CodeEdit:
+ be = value
+ else:
+ var sc : ScriptEditor = EditorInterface.get_script_editor()
+ var ed : ScriptEditorBase = sc.get_current_editor()
+ be= ed.get_base_editor()
+
+
+ if be is CodeEdit:
+ if !is_handled(be):
+ return
+
+ var parent : Node = be.get_parent()
+ var index : int = be.get_index()
+
+ if !is_instance_valid(parent):
+ return
+
+ if parent.get_child_count() > index + 1:
+ var c : Node = parent.get_child(index + 1)
+ if c is CodeEdit:
+ _on_focus(c, be)
+
+ c.queue_free()
+ parent.remove_child(c)
+ else:
+ if index > 0 and parent.get_child_count() > index:
+ var c : Node = parent.get_child(index - 1)
+ if c is CodeEdit:
+ _on_focus(c, be)
+
+ c.queue_free()
+ parent.remove_child(c)
+
+ if parent.get_child_count() == 1:
+ var p : Node = parent.get_parent()
+ if p:
+ for y : Node in parent.get_children():
+ if y.is_queued_for_deletion():
+ continue
+ if y.has_meta(&"RM"):
+ continue
+ if y is CodeEdit:
+ if y.text_changed.is_connected(_on_text_change):
+ y.text_changed.disconnect(_on_text_change)
+ parent.remove_child(y)
+ p.add_child(y)
+ if p.get_child_count() > 1:
+ p.move_child(y, 0)
+ _vsplits.erase(parent)
+ parent.queue_free()
+
+ for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ if x.has_method(&"_io_call"):
+ x.call(&"_io_call", &"")
+
+func _on_sub_split(__ : Variant = null) -> void:
+ var sc : ScriptEditor = EditorInterface.get_script_editor()
+ var ed : ScriptEditorBase = sc.get_current_editor()
+ var be : Control = ed.get_base_editor()
+
+ if be is CodeEdit:
+ var parent : Node = be.get_parent()
+ if is_handled(be) or !is_instance_valid(parent):
+ return
+
+ var z : int = 0
+ for x : Node in parent.get_children():
+ if x is CodeEdit:
+ z += 1
+ if z < 2:
+ var vsplit : VSplitContainer = null
+ if be.get_parent() is VSplitContainer:
+ vsplit = be.get_parent()
+ else:
+ vsplit = VSplitContainer.new()
+ var p : Node = be.get_parent()
+ if p:
+ p.remove_child(be)
+
+ parent.add_child(vsplit)
+ parent.move_child(vsplit, 0)
+ vsplit.add_child(be)
+
+ vsplit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ vsplit.size_flags_vertical= Control.SIZE_EXPAND_FILL
+
+ _vsplits.append(vsplit)
+
+ var ne : CodeEdit = be.duplicate(0)
+
+ ne.set_meta(&"RM", true)
+ ne.set_script(SspEditor)
+ ne.focus_mode = Control.FOCUS_CLICK
+ ne.mouse_filter = Control.MOUSE_FILTER_PASS
+
+ ne.selecting_enabled = false
+ var nodes : Array[Node] = be.get_parent().get_parent().get_parent().find_children("*","MenuButton",true,false)
+
+ for n : Node in nodes:
+ if n is MenuButton:
+ var mp : PopupMenu = n.get_popup()
+ if mp and "%" in (n.get_popup().get_item_text(0)):
+ if n.draw.is_connected(_on_update):
+ n.draw.disconnect(_on_update)
+ n.draw.connect(_on_update.bind(be,ne,n))
+
+ be.text_changed.connect(_on_text_change.bind(be, ne))
+
+ ne.focus_entered.connect(_on_focus.bind(ne, be))
+ ne.gui_input.connect(_on_gui.bind(ne, be))
+
+ _on_text_change(be, ne)
+ vsplit.add_child(ne)
+
+ for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ if x.has_method(&"_io_call"):
+ x.call(&"_io_call", &"")
+
+func _on_gui(e : InputEvent, f : CodeEdit, t : CodeEdit) -> void:
+ if t.has_focus():
+ if e.is_pressed():
+ if e is InputEventMouseButton:
+ if e.button_index == MOUSE_BUTTON_LEFT:
+ return
+ _on_focus(f, t)
+ else:
+ if e.is_pressed():
+ if e is InputEventMouseButton:
+ if e.button_index != MOUSE_BUTTON_RIGHT:
+ return
+ _on_focus(f, t)
+
+
+func _on_update(f : Variant, t : Variant, r : Variant) -> void:
+ if is_instance_valid(f) and is_instance_valid(t):
+ t.set(&"theme_override_font_sizes/font_size", f.get(&"theme_override_font_sizes/font_size"))
+ return
+ if is_instance_valid(r):
+ if r.draw.is_connected(_on_update):
+ r.draw.disconnect(_on_update)
+
+func _on_focus(f : CodeEdit, t : CodeEdit) -> void:
+ if !is_instance_valid(f) or !is_instance_valid(t):
+ return
+ if f.text != t.text:
+ var sv : float = f.scroll_vertical
+ var sh : int = f.scroll_horizontal
+ f.set(&"text", t.text)
+ f.scroll_vertical = sv
+ f.scroll_horizontal = sh
+ var sv0 : float = f.scroll_vertical
+ var sh0 : int = f.scroll_horizontal
+ var sv1 : float = t.scroll_vertical
+ var sh1 : int = t.scroll_horizontal
+ t.scroll_vertical = sv0
+ t.scroll_horizontal = sh0
+ f.scroll_vertical = sv1
+ f.scroll_horizontal = sh1
+ var index : int = t.get_index()
+ var p : Node = f.get_parent()
+ p.remove_child(f)
+ p.add_child(f)
+ t.grab_focus()
+
+ if p.get_child_count() > index or index == -1:
+ p.move_child(f, index)
+
+func _on_text_change(ca : CodeEdit, cb : CodeEdit) -> void:
+ if cb.has_method(&"set_text_reference"):
+ cb.call(&"set_text_reference", ca.text)
+ return
+ var sv : float = cb.scroll_vertical
+ var sh : int = cb.scroll_horizontal
+ cb.set(&"text", ca.text)
+ cb.scroll_vertical = sv
+ cb.scroll_horizontal = sh
+
+func _reorder(index : int, cd : CodeEdit, line : int, column : int) -> void:
+ if cd.get_caret_count() <= index:
+ cd.add_caret(mini(cd.get_line_count(), line), column)
+ return
+ cd.set_caret_line(mini(cd.get_line_count(), line), false, true, 0, index)
+ cd.set_caret_column(column, false, index)
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ for x : Node in _vsplits:
+ if !is_instance_valid(x):
+ continue
+ var p : Node = x.get_parent()
+ for y : Node in x.get_children():
+ if y.is_queued_for_deletion():
+ continue
+ if y.has_meta(&"RM"):
+ continue
+ if y is CodeEdit:
+ for cn : Dictionary in y.text_changed.get_connections():
+ var callable : Callable = cn["callable"]
+ if !callable.is_valid():
+ y.text_changed.disconnect(callable)
+
+ for n : Node in x.get_parent().get_parent().get_parent().find_children("*","MenuButton",true,false):
+ if n is MenuButton:
+ var mp : PopupMenu = n.get_popup()
+ if mp and "%" in (n.get_popup().get_item_text(0)):
+ for cn : Dictionary in n.draw.get_connections():
+ var callable : Callable = cn["callable"]
+ if !callable.is_valid():
+ n.draw.disconnect(callable)
+ x.remove_child(y)
+ p.add_child(y)
+ if p.get_child_count() > 1:
+ p.move_child(y, 0)
+ x.queue_free()
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd.uid
new file mode 100644
index 0000000..c5f4f98
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/ssp_window.gd.uid
@@ -0,0 +1 @@
+uid://cdn4c7qori2ry
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd
new file mode 100644
index 0000000..3a78f54
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd
@@ -0,0 +1,34 @@
+@tool
+extends EditorContextMenuPlugin
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+var CONTEXT : String = "CUSTOM"
+var ICON : Texture = null
+var SHORTCUT : Shortcut = null
+var CALLABLE : Callable
+var VALIDATOR : Callable
+
+func _init(context : String, handle : Callable, validator : Callable, icon : Texture, input_key : Array[InputEvent] = []):
+ CONTEXT = context
+ CALLABLE = handle
+ ICON = icon
+ VALIDATOR = validator
+ if input_key.size() > 0:
+ SHORTCUT = Shortcut.new()
+ SHORTCUT.events = input_key
+ add_menu_shortcut(SHORTCUT, handle)
+
+func _popup_menu(paths : Variant) -> void:
+ if VALIDATOR.is_valid():
+ if !VALIDATOR.call(paths):
+ return
+ if SHORTCUT:
+ add_context_menu_item_from_shortcut(CONTEXT, SHORTCUT, ICON)
+ else:
+ if CALLABLE.is_valid():
+ add_context_menu_item(CONTEXT, CALLABLE, ICON)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd.uid
new file mode 100644
index 0000000..47bcb74
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/contex/window.gd.uid
@@ -0,0 +1 @@
+uid://cblflkellpdqg
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd
new file mode 100644
index 0000000..ccbf44a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd
@@ -0,0 +1,23 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const MickeyTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd")
+const ToolDB = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd")
+const Manager = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd")
+
+var _tool_db : ToolDB = null
+var _manager : Manager = null
+
+func _init(manager : Manager, tool_db : ToolDB) -> void:
+ _manager = manager
+ _tool_db = tool_db
+
+func execute(_value : Variant = null) -> bool:
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd.uid
new file mode 100644
index 0000000..198c806
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd.uid
@@ -0,0 +1 @@
+uid://b5denwbu6twf4
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd
new file mode 100644
index 0000000..5809d99
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd
@@ -0,0 +1,104 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const EditorTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd")
+
+const HelperEditorTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd")
+const ScriptEditorTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd")
+const TextEditorTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd")
+
+var _tools : Array[EditorTool] = [
+ ScriptEditorTool.new(),
+ HelperEditorTool.new(),
+ TextEditorTool.new()
+]
+
+func execute(value : Variant = null) -> bool:
+ if !is_instance_valid(value) or !(value is Control):
+ return true
+
+ var control : Control = value
+
+ if !control.is_node_ready() or !control.is_inside_tree():
+ return false
+
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.has(control):
+ x.set_queue_free(false)
+ return true
+
+ var index : int = control.get_index()
+ if !_manager.is_valid_item_index(index):
+ return false
+
+ var root : Node = _get_root()
+
+ if is_instance_valid(root):
+ var mt : MickeyTool = _tools[0].build(control)
+ var is_editor : bool = _is_editor(mt, control)
+
+ if !is_editor:
+ for z : int in range(1, _tools.size(), 1):
+ var x : EditorTool = _tools[z]
+ mt = x.build(control)
+
+ if mt != null:
+ break
+
+ if mt != null:
+ mt.focus.connect(_manager.focus_tool)
+ mt.new_symbol.connect(_manager.set_symbol)
+ mt.clear.connect(_manager.clear_editors)
+ mt.ochorus(root)
+
+ _tool_db.append(mt)
+
+ _manager.tool_created()
+ _manager.update_metadata(mt)
+
+ mt.trigger_focus()
+ return false
+
+ if is_editor:
+ return true
+
+ printerr("Error!, Can not build control for ", control.name)
+ return false
+
+func _is_editor(mt : MickeyTool, control : Control) -> bool:
+ if is_instance_valid(mt):
+ return true
+
+ if control is ScriptEditorBase:
+ var sce : ScriptEditor = EditorInterface.get_script_editor()
+ if sce and control in sce.get_open_script_editors():
+ if control.name.begins_with("@"):
+ if !("Script" in control.name):
+ return false
+ return true
+ return _manager.get_editor_list().get_item_tooltip(control.get_index()).is_empty()
+
+ return false
+
+func _get_root() -> Node:
+ var root : Node = _manager.get_current_root()
+ if !is_instance_valid(root):
+ var splitters : Array[Node] = _manager.get_base_container().get_all_splitters()
+ if splitters.size() == 0:
+ for x : MickeyTool in _tool_db.get_tools():
+ x.reset()
+ _manager.get_base_container().initialize_editor_container()
+ root = _manager.get_current_root()
+ else:
+ for x : Node in splitters:
+ if is_instance_valid(x) and !x.is_queued_for_deletion():
+ root = x
+ break
+ return root
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd.uid
new file mode 100644
index 0000000..d875493
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd.uid
@@ -0,0 +1 @@
+uid://bsrituhgnfbm
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd
new file mode 100644
index 0000000..27fff67
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd
@@ -0,0 +1,24 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+#Override app function.
+func execute(value : Variant = null) -> bool:
+ if value is Array:
+ if value.size() > 1:
+ if value[0] is TabContainer and value[1] is int:
+ var control : TabContainer = value[0]
+ var index : int = value[1]
+ for x : MickeyTool in _tool_db.get_tools():
+ if is_instance_valid(x):
+ if x.get_root() == control:
+ if x.get_control().get_index() == index:
+ x.trigger_focus()
+ return true
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd.uid
new file mode 100644
index 0000000..2ee4c31
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd.uid
@@ -0,0 +1 @@
+uid://bipvmrq4th30m
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd
new file mode 100644
index 0000000..16e48c2
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd
@@ -0,0 +1,115 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const BaseList = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd")
+
+
+var unfocus_enabled : bool = true
+var unfocus_color : Color = Color.DARK_GRAY
+
+func _init(manager : Manager, tool_db : ToolDB) -> void:
+ super(manager, tool_db)
+ _setup()
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+func _on_change() -> void:
+ var dt : Array = ["plugin/script_splitter/editor/out_focus_color_enabled","plugin/script_splitter/editor/out_focus_color_value"]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ var current : Node = _manager.get_base_container().get_current_container()
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.is_valid():
+ var root : Control = x.get_root()
+ if root.modulate != Color.WHITE:
+ if unfocus_enabled:
+ root.modulate = unfocus_color
+ else:
+ root.modulate = Color.WHITE
+ elif unfocus_enabled:
+ if is_instance_valid(current):
+ if x.get_root() != current:
+ root.modulate = unfocus_color
+ break
+
+func _setup() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["unfocus_enabled", "plugin/script_splitter/editor/out_focus_color_enabled"]
+ ,["unfocus_color", "plugin/script_splitter/editor/out_focus_color_value"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+func execute(value : Variant = null) -> bool:
+ if value is ScriptEditorBase:
+ var control : Control = value.get_base_editor()
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.has(control):
+ value = x
+ break
+ if value is MickeyTool:
+ var index : int = value.get_index()
+ var editor_list : BaseList = _manager.get_editor_list()
+ if editor_list.item_count() > index and index > -1:
+ var control : Node = value.get_control()
+ var root : Node = value.get_root()
+ if root is TabContainer:
+ var base : Manager.BaseContainer = _manager.get_base_container()
+ var _index : int = control.get_index()
+ if root.current_tab != _index and _index > -1 and _index < root.get_tab_count():
+ if root.has_method(&"set_tab"):
+ root.call(&"set_tab", _index)
+ else:
+ root.set(&"current_tab", _index)
+
+ var container : Control = base.get_current_container()
+ if is_instance_valid(container) and unfocus_enabled:
+ container.modulate = unfocus_color
+
+ base.set_current_container(root)
+
+ if is_instance_valid(root):
+ root.modulate = Color.WHITE
+
+ var new_container : Node = base.get_container(root)
+ if is_instance_valid(new_container) and new_container.has_method(&"expand_splited_container"):
+ new_container.call(&"expand_splited_container", base.get_container_item(root))
+
+ if is_instance_valid(container):
+ container = base.get_container(container)
+ if is_instance_valid(container) and container != new_container and container.has_method(&"expand_splited_container"):
+ container.call(&"expand_splited_container", null)
+
+ var grant_conainer : Node = base.get_editor_root_container(new_container)
+ if is_instance_valid(grant_conainer):
+ var parent : Node = grant_conainer.get_parent()
+ if is_instance_valid(parent) and parent.has_method(&"expand_splited_container"):
+ parent.call(&"expand_splited_container", base.get_editor_root_container(new_container))
+
+ if !editor_list.is_selected(index):
+ editor_list.select(index)
+
+ _manager.io.update()
+ _manager.get_editor_list().updated.emit()
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd.uid
new file mode 100644
index 0000000..7a564f9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd.uid
@@ -0,0 +1 @@
+uid://bk4jykdijx7sj
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd
new file mode 100644
index 0000000..5eb629c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd
@@ -0,0 +1,219 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const BaseList = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd")
+
+var expanded : bool = false
+var _updating : bool = false
+
+func update() -> void:
+ if _updating:
+ return
+ _updating = true
+ _update.call_deferred()
+
+func _update() -> void:
+ var base : Manager.BaseContainer = _manager.get_base_container()
+ var container : Node = base.get_current_container()
+ if is_instance_valid(container):
+ if expanded:
+ var cb : Node = base.get_container_item(container)
+ var ct : Array[Node] = container.get_tree().get_nodes_in_group(&"__SP_BR__")
+ for x : Node in container.get_tree().get_nodes_in_group(&"__SP_IC__"):
+ var v : bool = cb == x
+ for y : Node in x.get_children():
+ if y is Control:
+ y.visible = v
+
+ for __ : int in range(0, 2, 1):
+ for x : Node in ct:
+ if x is Control:
+ var v : bool = false
+ for y : Node in x.get_children():
+ if y is Control and y.visible:
+ v = true
+ break
+ x.visible = v
+
+ var can_split : bool = _can_split(container)
+ var can_merge_column : bool = _can_merge_column(base)
+ var can_merge_row : bool = _can_merge_row(base)
+ var can_sub_split : int = _sub()
+
+ for x : Node in (Engine.get_main_loop()).get_nodes_in_group(&"__script_splitter__IO__"):
+ x.enable(&"SPLIT_COLUMN",can_split)
+ x.enable(&"MERGE_COLUMN",can_merge_column)
+ x.enable(&"SPLIT_ROW",can_split)
+ x.enable(&"MERGE_ROW",can_merge_row)
+ x.enable(&"SPLIT_SUB", can_sub_split == 0)
+ x.enable(&"MERGE_SPLIT_SUB", can_sub_split == 1)
+
+ _updating = false
+
+func _can_split(container : Node) -> bool:
+ return container != null and container.get_child_count() > 1
+
+func _can_merge_column(base : Manager.BaseContainer) -> bool:
+ return base != null and base.get_current_splitters().size() > 1
+
+func _can_merge_row(base : Manager.BaseContainer) -> bool:
+ return base != null and base.get_all_containers().size() > 1
+
+func _sub() -> int:
+ var sc : ScriptEditor = EditorInterface.get_script_editor()
+
+ if !is_instance_valid(sc.get_current_script()):
+ return -1
+
+ var ed : ScriptEditorBase = sc.get_current_editor()
+ var be : Control = ed.get_base_editor()
+
+
+ if be is CodeEdit:
+ if be.get_parent() is VSplitContainer:
+ return 1
+ return 0
+
+ return -1
+
+func _on_pin(btn : Button) -> void:
+ var st : String = btn.get_meta(&"I")
+ if st.is_empty():
+ btn.queue_free()
+ return
+
+ var bl : Manager.BaseList = _manager.get_editor_list()
+ for x : int in bl.item_count():
+ if st == bl.get_item_tooltip(x):
+ bl.select(x)
+ return
+
+func _make_pin(tree : SceneTree, fn : String, tp : String, icn : Texture2D, mod : Color) -> void:
+ if mod == Color.BLACK:
+ mod = Color.WHITE
+ for x : Node in tree.get_nodes_in_group(&"__SP_PIN_ROOT__"):
+ var btn : Button = Button.new()
+ btn.text = fn
+ btn.icon = icn
+ btn.set_meta(&"I", tp)
+ btn.pressed.connect(_on_pin.bind(btn))
+ btn.add_to_group(&"__SP_B_PIN__")
+ btn.set(&"theme_override_colors/icon_normal_color", mod)
+ btn.set(&"theme_override_colors/icon_focus_color", mod)
+ btn.set(&"theme_override_colors/icon_pressed_color", mod)
+ btn.set(&"theme_override_colors/icon_hover_color", mod)
+ btn.set(&"theme_override_colors/icon_hover_pressed_color", mod)
+ btn.set(&"theme_override_colors/icon_disabled_color", mod)
+ btn.set(&"theme_override_font_sizes/font_size", 12.0)
+ x.add_child(btn)
+
+func _remove_pin(tree : SceneTree, tp : String) -> bool:
+ for x : Node in tree.get_nodes_in_group(&"__SP_PIN_ROOT__"):
+ if x.has_meta(&"I"):
+ if x.get_meta(&"I") == tp:
+ x.queue_free()
+ return true
+ return false
+
+func execute(value : Variant = null) -> bool:
+ if value == null:
+ update()
+ return true
+
+ if value is StringName:
+ if value.is_empty():
+ update()
+ return true
+
+ var base : Manager.BaseContainer = _manager.get_base_container()
+ var container : Node = base.get_current_container()
+
+ var id : StringName = value
+
+ match id:
+ &"EXPAND":
+ if is_instance_valid(container):
+ var ct : Array[Node] = container.get_tree().get_nodes_in_group(&"__SP_BR__")
+ if expanded:
+ for x : Node in container.get_tree().get_nodes_in_group(&"__SP_IC__"):
+ for y : Node in x.get_children():
+ if y is Control:
+ y.visible = true
+ for x : Node in ct:
+ if x is Control:
+ x.visible = true
+ else:
+ var cb : Node = base.get_container_item(container)
+ for x : Node in container.get_tree().get_nodes_in_group(&"__SP_IC__"):
+ var v : bool = cb == x
+ for y : Node in x.get_children():
+ if y is Control:
+ y.visible = v
+
+ for __ : int in range(0, 2, 1):
+ for x : Node in ct:
+ if x is Control:
+ var v : bool = false
+ for y : Node in x.get_children():
+ if y is Control and y.visible:
+ v = true
+ break
+ x.visible = v
+
+ expanded = !expanded
+
+ for x : Node in container.get_tree().get_nodes_in_group(&"__script_splitter__IO__"):
+ if x.has_method(&"get_button"):
+ var button : Button = x.call(&"get_button", id)
+ if is_instance_valid(button):
+ if expanded:
+ button.modulate = Color.GREEN
+ else:
+ button.modulate = Color.WHITE
+
+ return true
+ &"PIN":
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.get_root() == container:
+ if container is TabContainer:
+ if container.current_tab == x.get_control().get_index():
+ var list : Manager.BaseList = _manager.get_editor_list()
+ var idx : int = x.get_index()
+
+ if list.item_count() > idx and idx > -1:
+ var nm : String = list.get_item_text(idx)
+ var ps : String = list.get_item_tooltip(idx)
+
+ if _remove_pin(container.get_tree(), ps):
+ return true
+
+ _make_pin(container.get_tree(), nm, ps, list.get_item_icon(idx), list.get_item_icon_modulate(idx))
+ &"SPLIT_COLUMN":
+ if _can_split(container):
+ _manager.split_column.execute()
+ &"SPLIT_ROW":
+ if _can_split(container):
+ _manager.split_row.execute()
+ &"MERGE_COLUMN":
+ if _can_merge_column(base):
+ _manager.merge_tool.execute([null, false])
+ &"MERGE_ROW":
+ if _can_merge_row(base):
+ _manager.merge_tool.execute([null, true])
+ &"SPLIT_SUB":
+ if _sub() == 0:
+ for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ x.script_split()
+ break
+ &"MERGE_SPLIT_SUB":
+ if _sub() == 1:
+ for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ x.script_merge()
+ break
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd.uid
new file mode 100644
index 0000000..ae5b166
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd.uid
@@ -0,0 +1 @@
+uid://dqgve5bbg0w1m
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd
new file mode 100644
index 0000000..dd88524
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd
@@ -0,0 +1,91 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func _get_tool(value : Variant) -> MickeyTool:
+ var container : MickeyTool = null
+
+ if value == null:
+ container = _tool_db.get_by_reference(_manager.get_base_container().get_current_container())
+ elif value is Node:
+ container = _tool_db.get_by_reference(value)
+ elif value is Resource:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value.resource_path
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ container = _tool_db.get_tool_id(x)
+ break
+ elif value is String:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ container = _tool_db.get_tool_id(x)
+ break
+
+ return container
+
+func execute(value : Variant = null) -> bool:
+ if value is Array:
+ var mk : MickeyTool = _get_tool(value[0])
+
+ if is_instance_valid(mk) and value[1] is bool:
+ if mk and mk.is_valid():
+ var root : Node = mk.get_root()
+ var control : Node = root
+ if control.is_in_group(&"__SC_SPLITTER__"):
+ var cbase : Manager.BaseContainer = _manager.get_base_container()
+ if value[1]:
+ control = cbase.get_container(control)
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.is_valid():
+ var node : Control = x.get_root()
+ if control == cbase.get_container(node):
+ x.reset()
+ control.queue_free()
+ else:
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.is_valid():
+ var node : Control = x.get_root()
+ if node:
+ if node == control:
+ x.reset()
+ else:
+ x.reset()
+
+ var base : Manager.BaseContainer = _manager.get_base_container()
+
+ if root == base.get_current_container():
+ var nodes : Array[Node] = control.get_tree().get_nodes_in_group(&"__SC_SPLITTER__")
+ var container : Node = base.get_container_item(root)
+
+ for n : Node in nodes:
+ if n == root:
+ continue
+ var _container : Node = base.get_container_item(n)
+ if _container.get_parent() == container.get_parent():
+ var i0 : int = _container.get_index()
+ var i1 : int = container.get_index()
+ if i0 == i1 - 1 or i0 == i1 + 1:
+ base.set_current_container(n)
+ return true
+
+ var z : int = nodes.find(root)
+ if z != -1:
+ if z == 0:
+ if nodes.size() > 1:
+ base.set_current_container(nodes[1])
+ else:
+ if nodes.size() > 1:
+ base.set_current_container(nodes[z - 1])
+ return true
+ #if control.get_child_count() == 0 or root.get_child_count() == 0:
+ #control.queue_free()
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd.uid
new file mode 100644
index 0000000..35ca41e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd.uid
@@ -0,0 +1 @@
+uid://cf43swgi3ydv8
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd
new file mode 100644
index 0000000..42debe0
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd
@@ -0,0 +1,77 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+var _refreshing : bool = true
+
+func _init(manager : Manager, tool_db : ToolDB) -> void:
+ super(manager, tool_db)
+ _setup()
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+func _on_change() -> void:
+ var dt : Array = ["plugin/script_splitter/behaviour/refresh_warnings_on_save"]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ break
+
+func _setup() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["_refreshing", "plugin/script_splitter/behaviour/refresh_warnings_on_save"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+func execute(_value : Variant = null) -> bool:
+ if !_refreshing:
+ return true
+
+ var sp : Array[Node] = Engine.get_main_loop().get_nodes_in_group(&"__SC_SPLITTER__")
+ var current : Control = _manager.get_base_container().get_current_container()
+
+ var ctool : MickeyTool = null
+ var ltool : MickeyTool = null
+
+ if sp.size() < 2:
+ return true
+
+ for x : Variant in _tool_db.get_tools():
+ if is_instance_valid(x):
+ if x.is_valid():
+ var i : int = sp.find(x.get_root())
+ var container : Node = sp[i]
+ if container is TabContainer:
+ var indx : int = x.get_control().get_index()
+ if container.current_tab == indx:
+ if container == current:
+ ctool = x
+ ltool = x
+ _manager.select_editor_by_index(x.get_index())
+
+
+ if is_instance_valid(ctool) and ctool != ltool:
+ _manager.select_editor_by_index(ctool.get_index())
+
+ return true
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd.uid
new file mode 100644
index 0000000..b23bd46
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd.uid
@@ -0,0 +1 @@
+uid://c0wasvo7fwcqr
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd
new file mode 100644
index 0000000..3f48d89
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd
@@ -0,0 +1,26 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func execute(value : Variant = null) -> bool:
+ if value is Array:
+ var control : Control = value[0]
+ var index : int = value[1]
+
+ if index < 0:
+ return false
+
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.get_root() == control and x.get_control().get_index() == index:
+ var _index : int = x.get_index()
+ x.reset()
+ _manager.get_editor_list().remove(_index)
+ return true
+ return false
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd.uid
new file mode 100644
index 0000000..631d782
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd.uid
@@ -0,0 +1 @@
+uid://bht1hix6hophq
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd
new file mode 100644
index 0000000..146dac3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd
@@ -0,0 +1,21 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func execute(value : Variant = null) -> bool:
+ if value is Array:
+ if value[0] is Control and value[1] is int:
+ if value[1] < 0:
+ return false
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.get_index() == value[1]:
+ if x.is_valid():
+ x.ochorus(value[0])
+ return true
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd.uid
new file mode 100644
index 0000000..1c1e1cf
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd.uid
@@ -0,0 +1 @@
+uid://ckvujn0hnsm11
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd
new file mode 100644
index 0000000..81db7e4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd
@@ -0,0 +1,30 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func execute(value : Variant = null) -> bool:
+ if value is Array:
+ var idx : int = value[0]
+ var node : Node = value[1]
+
+ if idx < 0:
+ return false
+
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.get_root() == node:
+ if x.get_control().get_index() == idx:
+ var list : Manager.BaseList = _manager.get_editor_list()
+ var indx : int = x.get_index()
+ if list.item_count() > indx and indx > -1:
+ var el : ItemList = list.get_editor_list()
+ el.item_clicked.emit(indx,el.get_local_mouse_position(), MOUSE_BUTTON_RIGHT)
+
+ return true
+
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd.uid
new file mode 100644
index 0000000..2b6a7a5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd.uid
@@ -0,0 +1 @@
+uid://dj5eoum4nippb
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd
new file mode 100644
index 0000000..1ea0aee
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd
@@ -0,0 +1,28 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func execute(value : Variant = null) -> bool:
+ if value is int:
+ if value < 0:
+ return false
+
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.get_index() == value:
+ var root : Variant = x.get_root()
+ if is_instance_valid(root):
+ if root is TabContainer:
+ var index : int = x.get_control().get_index()
+ if root.current_tab != index and index > -1 and root.get_tab_count() > index:
+ if root.has_method(&"set_tab"):
+ root.call(&"set_tab", index)
+ else:
+ root.current_tab = index
+ return true
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd.uid
new file mode 100644
index 0000000..240bcfa
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd.uid
@@ -0,0 +1 @@
+uid://bej35a842s2yd
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd
new file mode 100644
index 0000000..d883f68
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd
@@ -0,0 +1,44 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+func execute(value : Variant = null) -> bool:
+ var _tool : MickeyTool = null
+ if value == null:
+ value = _manager.get_base_container().get_current_container()
+ elif value is Resource:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value.resource_path
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ _tool = _tool_db.get_tool_id(x)
+ break
+ elif value is String:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ _tool = _tool_db.get_tool_id(x)
+ break
+
+ if _tool == null:
+ if value is MickeyTool:
+ _tool = value
+ elif value is Node:
+ _tool = _tool_db.get_by_reference(value)
+
+ if is_instance_valid(_tool) and _tool.is_valid():
+ if _manager._focus_tool.unfocus_enabled:
+ _tool.get_root().modulate = _manager._focus_tool.unfocus_color
+ var idx : int = _tool.get_index()
+ if idx > -1 and _manager.get_editor_list().item_count() > idx:
+ _manager.move_tool(_manager.get_base_container().new_column(), idx)
+ _manager.io.update()
+ return true
+
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd.uid
new file mode 100644
index 0000000..9e0a782
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd.uid
@@ -0,0 +1 @@
+uid://bomyp1t030hd
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd
new file mode 100644
index 0000000..706d604
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd
@@ -0,0 +1,45 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+func execute(value : Variant = null) -> bool:
+ var _tool : MickeyTool = null
+
+ if value == null:
+ value = _manager.get_base_container().get_current_container()
+ elif value is Resource:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value.resource_path
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ _tool = _tool_db.get_tool_id(x)
+ break
+ elif value is String:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ _tool = _tool_db.get_tool_id(x)
+ break
+
+ if _tool == null:
+ if value is MickeyTool:
+ _tool = value
+ elif value is Node:
+ _tool = _tool_db.get_by_reference(value)
+
+ if is_instance_valid(_tool) and _tool.is_valid():
+ if _manager._focus_tool.unfocus_enabled:
+ _tool.get_root().modulate = _manager._focus_tool.unfocus_color
+ var idx : int = _tool.get_index()
+ if idx > -1 and _manager.get_editor_list().item_count() > idx:
+ _manager.move_tool(_manager.get_base_container().new_row(), idx)
+ _manager.io.update()
+ return true
+
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd.uid
new file mode 100644
index 0000000..bf4ce13
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd.uid
@@ -0,0 +1 @@
+uid://bg2573oxujrny
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd
new file mode 100644
index 0000000..057cccf
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd
@@ -0,0 +1,71 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const BaseContainer = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd")
+
+
+func execute(value : Variant = null) -> bool:
+ if value is Array:
+ if value.size() == 3:
+ if value[0] is Container and value[1] is int and value[2] is Container:
+ var from : Container = value[0]
+ var index : int = value[1]
+ var to : Container = value[2]
+
+ if from == to:
+ return false
+
+ if from is BaseContainer.SplitterContainer.SplitterEditorContainer.Editor and to is BaseContainer.SplitterContainer.SplitterEditorContainer.Editor:
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.is_valid():
+ if x.get_root() == from and x.get_control().get_index() == index:
+ x.ochorus(to)
+ _manager.clear_editors()
+ return true
+ else:
+ if value[0] is String and value[1] is String and value[2] is bool:
+ var base : Manager.BaseList = _manager.get_editor_list()
+ var from : String = value[0]
+ var left : bool = value[2]
+ var to : String = value[1]
+ var fm : MickeyTool = null
+ var tm : MickeyTool = null
+ if from == to:
+ return false
+ for x : MickeyTool in _tool_db.get_tools():
+ if !x.is_valid():
+ continue
+ var t : String = base.get_item_tooltip(x.get_index())
+ if from == t:
+ fm = x
+ elif to == t:
+ tm = x
+ if is_instance_valid(fm) and is_instance_valid(tm) and fm != tm:
+ var froot : Node = fm.get_root()
+ var troot : Node = tm.get_root()
+ if froot == troot:
+ var tidx : int = tm.get_control().get_index()
+ if left:
+ froot.move_child(fm.get_control(), maxi(tidx - 1,0))
+ else:
+ froot.move_child(fm.get_control(), tidx)
+ else:
+ if froot.get_child_count() == 1:
+
+ if _manager.merge_tool.execute([tm.get_control(), froot.get_parent().get_child_count() == 1]):
+ fm.ochorus(troot)
+ #if froot.get_parent().get_child_count() == 1:
+ #froot.get_parent().queue_free()
+ #else:
+ #froot.queue_free()
+ #_manager.get_base_container().update_split_container()
+ else:
+ fm.ochorus(troot)
+ return true
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd.uid
new file mode 100644
index 0000000..ff85dd1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd.uid
@@ -0,0 +1 @@
+uid://cwgbj8fqlg6wm
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd
new file mode 100644
index 0000000..8d20f2f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd
@@ -0,0 +1,105 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+var LIST_VISIBLE_SELECTED_COLOR : Color = Color.from_string("7b68ee", Color.CORNFLOWER_BLUE)
+var LIST_VISIBLE_OTHERS_COLOR : Color = Color.from_string("4835bb", Color.DARK_BLUE)
+
+
+var _script_list_selection : bool = false
+
+func _init(manager : Manager, tool_db : ToolDB) -> void:
+ super(manager, tool_db)
+ _setup()
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+func _on_change() -> void:
+ var dt : Array = [
+ "plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/selected_color"
+ ,"plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/others_color"
+ ]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ break
+
+func _setup() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["LIST_VISIBLE_SELECTED_COLOR", "plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/selected_color"]
+ ,["LIST_VISIBLE_OTHERS_COLOR", "plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/others_color"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+
+func execute(value : Variant = null) -> bool:
+ if !value is Array or value.size() < 1:
+ return false
+
+ if _script_list_selection:
+ return true
+
+ _script_list_selection = true
+
+
+ var _editor_list : ItemList = value[0]
+ var _script_list : ItemList = value[1]
+
+ var selected : String = ""
+ var others_selected : PackedStringArray = []
+
+
+ var current : TabContainer = _manager.get_base_container().get_current_container()
+
+
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.is_valid():
+ var _root : Node = x.get_root()
+ if _root.current_tab == x.get_control().get_index():
+ var idx : int = x.get_index()
+ if _editor_list.item_count > idx and idx > -1:
+ if _root == current:
+ selected = _editor_list.get_item_tooltip(idx)
+ else:
+ others_selected.append(_editor_list.get_item_tooltip(idx))
+
+ var color : Color = LIST_VISIBLE_SELECTED_COLOR
+ var color_ctn : Color = LIST_VISIBLE_SELECTED_COLOR
+ var others : Color = LIST_VISIBLE_OTHERS_COLOR
+ color.a = 0.5
+ others.a = 0.5
+ color_ctn.a = 0.25
+
+ for x : int in _script_list.item_count:
+ var mt : String = _script_list.get_item_tooltip(x)
+ if selected == mt:
+ _script_list.set_item_custom_bg_color(x, color)
+ _script_list.set_item_custom_fg_color(x, Color.WHITE)
+ _script_list.select(x, true)
+ elif others_selected.has(mt):
+ _script_list.set_item_custom_bg_color(x, others)
+ else:
+ _script_list.set_item_custom_bg_color(x, Color.TRANSPARENT)
+ _script_list.ensure_current_is_visible()
+ set_deferred(&"_script_list_selection", false)
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd.uid
new file mode 100644
index 0000000..6b1f8d0
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd.uid
@@ -0,0 +1 @@
+uid://d0fdvav3whi4t
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd
new file mode 100644
index 0000000..d5607a1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd
@@ -0,0 +1,80 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const BaseList = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd")
+var _buffer : Dictionary = {}
+
+func execute(value : Variant = null) -> bool:
+ var list : BaseList = _manager.get_editor_list()
+ if is_instance_valid(value) and value is MickeyTool:
+ _update(value, list)
+ else:
+ var arr : Array[MickeyTool] = _tool_db.get_tools()
+ for x : int in range(arr.size() - 1, -1, -1):
+ var _tool : Variant = arr[x]
+ if !is_instance_valid(_tool):
+ arr.remove_at(x)
+ continue
+ _update(_tool, list)
+
+ var dict : Dictionary = {}
+ for x : ToolDB.MickeyTool in _tool_db.get_tools():
+ if !x.is_valid():
+ continue
+ var _root : Node = x.get_root_control()
+ if dict.has(_root):
+ continue
+ dict[_root] = true
+ if _root.has_method(&"update"):
+ _root.call_deferred(&"update")
+ return true
+
+func _update(mk : MickeyTool, list : BaseList) -> void:
+ if !is_instance_valid(mk) or !mk.is_valid():
+ return
+ var index : int = mk.get_index()
+ if index > -1 and list.item_count() > index:
+ var icon : Texture2D = list.get_item_icon(index)
+ var modulate : Color = list.get_item_icon_modulate(index)
+ if icon and modulate != Color.WHITE and modulate != Color.BLACK:
+ var root : Node = mk.get_root()
+ var make : bool = true
+ if root.has_method(&"set_icon_color"):
+ make = root.call(&"set_icon_color", modulate)
+ if make:
+ if _buffer.has(icon):
+ icon = _buffer[icon]
+ else:
+ var new_icon : Texture2D = mod_image(icon, modulate)
+ _buffer[icon] = new_icon
+ icon = new_icon
+
+ mk.update_metadata(
+ list.get_item_text(index),
+ list.get_item_tooltip(index),
+ icon
+ )
+
+func mod_image(icon: Texture2D, modulate_color: Color) -> Texture2D:
+ var image : Image = icon.get_image()
+ if image.get_format() != Image.FORMAT_RGBA8:
+ image.convert(Image.FORMAT_RGBA8)
+
+ var width : int = image.get_width()
+ var height : int = image.get_height()
+
+ for x : int in range(width):
+ for y : int in range(height):
+ var original_color: Color = image.get_pixel(x, y)
+ var modulated_color: Color = modulate_color
+ if original_color.a > 0.0:
+ modulated_color.a = original_color.a
+ image.set_pixel(x, y, modulated_color)
+
+ return ImageTexture.create_from_image(image)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd.uid
new file mode 100644
index 0000000..a51432c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd.uid
@@ -0,0 +1 @@
+uid://bk6hirh5yekc5
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd
new file mode 100644
index 0000000..6ec3022
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd
@@ -0,0 +1,63 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/app.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func execute(arr : Variant = null) -> bool:
+ var value : Variant = arr[0]
+ var type : int = arr[1]
+
+ var _tool : MickeyTool = null
+
+ if value == null:
+ value = _manager.get_base_container().get_current_container()
+
+ elif value is Resource:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value.resource_path
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ _tool = _tool_db.get_tool_id(x)
+ break
+ elif value is String:
+ var list : ItemList = _manager.get_editor_list().get_editor_list()
+ var pth : String = value
+ for x : int in list.item_count:
+ if pth == list.get_item_tooltip(x):
+ _tool = _tool_db.get_tool_id(x)
+ break
+
+ if _tool == null:
+ if value is MickeyTool:
+ _tool = value
+ elif value is Node:
+ _tool = _tool_db.get_by_reference(value)
+
+ if is_instance_valid(_tool):
+ var root : Node = _tool.get_root()
+ var indx : int = _tool.get_control().get_index()
+
+ var index : PackedInt32Array = []
+
+ for x : MickeyTool in _tool_db.get_tools():
+ if x.get_root() == root:
+ if type < 0:
+ if x.get_control().get_index() < indx:
+ index.append(x.get_index())
+ elif type > 0:
+ if x.get_control().get_index() > indx:
+ index.append(x.get_index())
+ else:
+ if x.get_control().get_index() != indx:
+ index.append(x.get_index())
+
+ index.sort()
+
+ for z : int in range(index.size() - 1, -1, -1):
+ _manager.get_editor_list().remove(index[z])
+ return false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd.uid
new file mode 100644
index 0000000..d26c457
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd.uid
@@ -0,0 +1 @@
+uid://c8c77dtgjpvxr
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd
new file mode 100644
index 0000000..d6a22c4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd
@@ -0,0 +1,23 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+var _task : Array[Callable] = []
+
+func has(callable : Callable) -> bool:
+ return _task.has(callable)
+
+func add(task : Callable) -> void:
+ if task.is_valid():
+ _task.append(task)
+
+func update() -> void:
+ for task : Callable in _task:
+ if task.is_valid():
+ task.call()
+ _task.clear()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd.uid
new file mode 100644
index 0000000..0f41e24
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd.uid
@@ -0,0 +1 @@
+uid://qbsuad1aohqy
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd
new file mode 100644
index 0000000..40cb9da
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd
@@ -0,0 +1,64 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const MickeyTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd")
+
+var _tools : Array[MickeyTool] = []
+
+func get_tools() -> Array[MickeyTool]:
+ return _tools
+
+func append(mk : MickeyTool) -> void:
+ _tools.append(mk)
+
+func garbage(val : int) -> void:
+ if val == 1:
+ for x : Variant in _tools:
+ if is_instance_valid(x):
+ (x as MickeyTool).set_queue_free(true)
+ elif val == 0:
+ for x : int in range(_tools.size() - 1, -1, -1):
+ var variant : Variant = _tools[x]
+ if !is_instance_valid(variant):
+ _tools.remove_at(x)
+
+ if !variant.is_valid():
+ if !is_instance_valid(variant.get_owner()):
+ var root : Node = variant.get_root()
+ if is_instance_valid(root):
+ variant.get_root().queue_free()
+ variant.set_queue_free(true)
+
+ if (variant as MickeyTool).is_queue_free():
+ _tools.remove_at(x)
+
+func get_tool_id(id : int) -> MickeyTool:
+ for x : MickeyTool in _tools:
+ if x.get_index() == id:
+ return x
+ return null
+
+func has_tool_id(id : int) -> bool:
+ for x : MickeyTool in _tools:
+ if x.get_index() == id:
+ return true
+ return false
+
+
+func clear() -> void:
+ for x : MickeyTool in _tools:
+ if is_instance_valid(x):
+ x.reset()
+ _tools.clear()
+
+func get_by_reference(control : Node) -> MickeyTool:
+ for x : MickeyTool in _tools:
+ if x.has(control):
+ return x
+ return null
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd.uid
new file mode 100644
index 0000000..24e16c3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd.uid
@@ -0,0 +1 @@
+uid://dv6xyd03fg7kj
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd
new file mode 100644
index 0000000..47fd948
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd
@@ -0,0 +1,361 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const CreateTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/create_tool.gd")
+const UpdateMetadata = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_metadata.gd")
+const FocusTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_tool.gd")
+const SelectByIndex = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/select_by_index.gd")
+const FocusByTab = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/focus_by_tab.gd")
+const ReparentTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/reparent_tool.gd")
+const MergeTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/merge_tool.gd")
+const SplitColumn = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_column.gd")
+const SplitRow = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/split_row.gd")
+const RemoveByTab = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/remove_by_tab.gd")
+const RefreshWarnings = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/refresh_warnings.gd")
+const UpdateListSelection = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/update_list_selection.gd")
+const SwapTab = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/swap_tab.gd")
+const RmbMenu = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/rmb_menu.gd")
+const UserTabClose = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/user_tab_close.gd")
+const Io = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/application/io.gd")
+
+const ToolDB = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/database/tool_db.gd")
+const Task = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/coroutine/task.gd")
+
+const BaseContainer = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd")
+const BaseList = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/list.gd")
+
+
+signal update_request()
+
+# API
+var split_column : SplitColumn = null
+var split_row : SplitRow = null
+var refresh_warnings : RefreshWarnings = null
+var merge_tool : MergeTool = null
+
+# APPLICATION
+var _create_tool : CreateTool = null
+var _focus_tool : FocusTool = null
+var _update_metadata : UpdateMetadata = null
+var _select_by_index : SelectByIndex = null
+var _focus_by_tab : FocusByTab = null
+var _remove_by_tab : RemoveByTab = null
+var _reparent_tool : ReparentTool = null
+var _update_list_selection : UpdateListSelection = null
+var _rmb_menu : RmbMenu = null
+var _user_tab_close : UserTabClose = null
+var io : Io = null
+var swap_tab : SwapTab = null
+
+# DB
+var _tool_db : ToolDB = null
+
+# REF
+var _base_container : BaseContainer = null
+var _base_list : BaseList = null
+var _task : Task = null
+
+func _app_setup() -> void:
+ _task = Task.new()
+ _tool_db = ToolDB.new()
+
+ _focus_tool = FocusTool.new(self, _tool_db)
+ _update_metadata = UpdateMetadata.new(self, _tool_db)
+ _create_tool = CreateTool.new(self, _tool_db)
+ _select_by_index = SelectByIndex.new(self, _tool_db)
+ _focus_by_tab = FocusByTab .new(self, _tool_db)
+ _reparent_tool = ReparentTool.new(self, _tool_db)
+ merge_tool = MergeTool.new(self, _tool_db)
+ _remove_by_tab = RemoveByTab.new(self, _tool_db)
+ _update_list_selection = UpdateListSelection.new(self, _tool_db)
+ swap_tab = SwapTab.new(self, _tool_db)
+ _rmb_menu = RmbMenu.new(self, _tool_db)
+ _user_tab_close = UserTabClose.new(self, _tool_db)
+ io = Io.new(self, _tool_db)
+
+ split_column = SplitColumn.new(self, _tool_db)
+ split_row = SplitRow.new(self, _tool_db)
+ refresh_warnings = RefreshWarnings.new(self, _tool_db)
+
+
+ _base_list.update_selections_callback = _update_list_selection.execute
+
+func update_list(__ : Variant) -> void:
+ _base_list.update_list_selection()
+
+func get_current_tool(ref : Node = null) -> ToolDB.MickeyTool:
+ if ref == null:
+ ref = _base_container.get_current_container()
+ return _tool_db.get_by_reference(ref)
+
+func _init(base_container : BaseContainer, base_list : BaseList) -> void:
+ _base_container = base_container
+ _base_list = base_list
+
+ #_base_list.set_handler(self)
+#
+ _base_list.updated.connect(update_all_metadata)
+ _base_list.item_selected.connect(_on_item_selected)
+ _base_container.update.connect(update_metadata)
+ _base_container.focus_by_tab.connect(_on_focus_tab)
+ _base_container.remove_by_tab.connect(_on_remove_tab)
+
+ _base_container.swap_tab.connect(_onswap_tab)
+ _base_container.change_container.connect(update_list)
+
+ _base_container.rmb_click.connect(_on_tab_rmb)
+
+ _base_container.exiting.connect(_on_exiting)
+
+ _app_setup()
+
+func _on_exiting() -> void:
+ _tool_db.clear()
+
+func get_total_editors() -> int:
+ var container : Control = _base_container.get_current_container()
+ if is_instance_valid(container):
+ return container.get_child_count()
+ return 0
+
+func get_current_totaL_editors(current : Node) -> int:
+ var container : Control = null
+
+ if current == null:
+ container = _base_container.get_current_container()
+ elif current is Node:
+ container = _tool_db.get_by_reference(current).get_root()
+
+ if is_instance_valid(container):
+ return container.get_child_count()
+ return 0
+
+func get_total_split_container(by_row : bool) -> int:
+ if by_row:
+ var rows : Array = []
+ for x : Node in _base_container.get_all_containers():
+ var parent : Node = x.get_parent()
+ if parent:
+ if !rows.has(parent):
+ rows.append(parent)
+ return rows.size()
+ else:
+ return _base_container.get_all_containers().size()
+
+func get_total_splitters() -> int:
+ return _base_container.get_all_splitters().size()
+
+func get_current_total_splitters(current : Node) -> int:
+ if current is CodeEdit:
+ var container : Control = null
+ var value : ToolDB.MickeyTool = _tool_db.get_by_reference(current)
+ if is_instance_valid(value) and value.is_valid():
+ container = _base_container.get_container(value.get_root())
+ if is_instance_valid(container):
+ return container.get_child_count()
+ return 0
+ return _base_container.get_current_splitters().size()
+
+func clear() -> void:
+ _tool_db.clear()
+
+func reset() -> void:
+ _tool_db.clear()
+
+ _base_container.reset()
+ _base_list.reset()
+
+func _onswap_tab(from : Container, index : int, to : Container) -> void:
+ swap_tab.execute([from, index, to])
+
+func _on_focus_tab(tab : TabContainer, index : int) -> void:
+ _focus_by_tab.execute([tab, index])
+
+func _on_remove_tab(tab : TabContainer, index : int) -> void:
+ _remove_by_tab.execute([tab, index])
+
+func _on_item_selected(i : int) -> void:
+ _select_by_index.execute(i)
+
+func is_valid_item_index(index : int) -> bool:
+ return index > -1 and _base_list.item_count() > index and !_base_list.get_item_tooltip(index).is_empty() and !_base_list.get_item_text(index).is_empty()
+
+func update() -> bool:
+ if !_base_container.is_active():
+ return false
+
+ _task.update()
+ _tool_db.garbage(1)
+
+ var update_required : bool = false
+ for x : Node in _base_container.get_editors():
+ update_required = !_create_tool.execute(x) || update_required
+
+ _tool_db.garbage(0)
+ _base_container.garbage()
+
+ _select_by_index.execute(_base_list.get_selected_id())
+
+ _update_root()
+
+ _base_container.update_split_container()
+ _base_list.update_list()
+ return !update_required
+
+# API
+func set_symbol(__ : String) -> void:
+ var tl : ToolDB.MickeyTool = _tool_db.get_tool_id(_base_list.get_selected_id())
+ if is_instance_valid(tl):
+ _focus_tool.execute(tl)
+ var gui : Node = tl.get_gui()
+ if gui is CodeEdit:
+ _center.call_deferred(gui)
+ else:
+ for x : Node in gui.get_children():
+ if x is RichTextLabel:
+ _center.call_deferred(x)
+
+func _center(gui : Variant) -> void:
+ if is_instance_valid(gui):
+ if gui is CodeEdit:
+ if gui.get_caret_count() > 0:
+ gui.scroll_vertical = gui.get_scroll_pos_for_line(maxi(gui.get_caret_line(0) - 1, 0))
+ gui.center_viewport_to_caret.call_deferred(0)
+
+func unsplit_column(current : Variant) -> void:
+ if merge_tool.execute([current, false]):
+ update_request.emit()
+
+func unsplit_row(current : Variant) -> void:
+ if merge_tool.execute([current, true]):
+ update_request.emit()
+
+func move_tool(control : Control, index : int) -> bool:
+ return _reparent_tool.execute([control, index])
+
+func get_current_root() -> Control:
+ return _base_container.get_current_editor()
+
+func get_editor_list() -> BaseList:
+ return _base_list
+
+func get_base_container() -> BaseContainer:
+ return _base_container
+
+func get_editor_container() -> TabContainer:
+ return _base_container.get_editor_container()
+
+func select_editor_by_index(index : int) -> void:
+ _base_list.select(index)
+
+func focus_tool(mk : Variant) -> void:
+ _focus_tool.execute(mk)
+
+func tool_created() -> void:
+ _base_container.tool_created()
+
+func update_metadata(mk : Variant = null) -> void:
+ _task.add(_update_metadata.execute.bind(mk))
+ update_request.emit()
+
+func update_all_metadata() -> void:
+ if !_task.has(_update_metadata.execute):
+ _task.add(_update_metadata.execute)
+ update_request.emit()
+
+func clear_editors() -> void:
+ if !_task.has(_clear_editor):
+ _task.add(_clear_editor)
+ update_request.emit()
+
+func _clear_editor() -> void:
+ var spls : Array[Node] = _base_container.get_all_splitters()
+ var total : int = spls.size()
+
+ if total > 1:
+ total = 0
+ for x : Node in spls:
+ if is_instance_valid(x):
+ if x.is_queued_for_deletion():
+ continue
+ total += 1
+
+ if total > 1:
+ for x : Node in spls:
+ if x.get_child_count() == 0:
+ if total < 2:
+ return
+
+ var c : Node = _base_container.get_container_item(x)
+ if c and !c.is_queued_for_deletion():
+ var container : Node = _base_container.get_container(x)
+ if container and container.get_child_count() < 2:
+ container.get_parent().queue_free()
+ c.queue_free()
+ total -= 1
+
+func _update_root() -> void:
+ var root : Control = _base_container.get_root_container()
+
+ if root:
+ var v : bool = false
+ var nodes : Array[Node] = root.get_tree().get_nodes_in_group(&"__SP_IC__")
+ var total : int = nodes.size()
+ for x : Node in nodes:
+ if total < 2:
+ break
+ if x.get_child_count() == 0:
+ x.queue_free()
+ total -= 1
+ else:
+ if x.get_child(0).get_child_count() == 0:
+ x.queue_free()
+ total -= 1
+
+ for x : Node in _base_container.get_all_splitters():
+ if x.get_child_count() > 0:
+ v = true
+ break
+ root.get_parent().visible = v
+
+func get_control_tool_by_current(current : Variant) -> Node:
+ var root : Node = null
+ if null == current or current is PackedStringArray and current.size() == 0:
+ current = get_base_container().get_current_container()
+ if current is TabContainer:
+ var i : int = current.current_tab
+ if i > -1:
+ current = current.get_child(i)
+ if current:
+ if current is String:
+ for x : int in _base_list.item_count():
+ if current == _base_list.get_item_tooltip(x):
+ var mk : ToolDB.MickeyTool = _tool_db.get_tool_id(x)
+ if mk:
+ root = mk.get_control()
+ break
+ elif current is Node:
+ for x : ToolDB.MickeyTool in _tool_db.get_tools():
+ if x.has(current):
+ root = x.get_control()
+ break
+ return root
+
+func _on_tab_rmb(index : int, tab : TabContainer) -> void:
+ _rmb_menu.execute([index, tab])
+
+func left_tab_close(value : Variant) -> void:
+ _user_tab_close.execute([value, -1])
+
+func right_tab_close(value : Variant) -> void:
+ _user_tab_close.execute([value, 1])
+
+func others_tab_close(value : Variant) -> void:
+ _user_tab_close.execute([value, 0])
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd.uid
new file mode 100644
index 0000000..eccf3d0
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/godot/manager.gd.uid
@@ -0,0 +1 @@
+uid://blq08yud6jfse
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd
new file mode 100644
index 0000000..e33d364
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd
@@ -0,0 +1,18 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const MickeyTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd")
+const MickeyToolRoute = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd")
+
+func build(control : Node) -> MickeyTool:
+ return _build_tool(control)
+
+func _build_tool(_control : Node) -> MickeyTool:
+ return null
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd.uid
new file mode 100644
index 0000000..135f612
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd.uid
@@ -0,0 +1 @@
+uid://byxd23l74ehqq
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd
new file mode 100644
index 0000000..4c82973
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd
@@ -0,0 +1,49 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func _build_tool(control : Node) -> MickeyTool:
+ if control is ScriptEditorBase:
+ return null
+ if control.name.begins_with("@"):
+ return null
+
+ var mickey : MickeyTool = null
+ for x : Node in control.get_children():
+ if x is RichTextLabel:
+ var canvas : VBoxContainer = VBoxContainer.new()
+ canvas.size_flags_vertical = Control.SIZE_EXPAND_FILL
+ canvas.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ var childs : Array[Node] = control.get_children()
+ for n : Node in childs:
+ control.remove_child(n)
+ canvas.add_child(n)
+
+ canvas.size = control.size
+ mickey = MickeyToolRoute.new(control, canvas, canvas)
+ break
+ return mickey
+
+func _handler(control : Node) -> MickeyTool:
+ var mickey : MickeyTool = null
+ if control is RichTextLabel:
+ var canvas : VBoxContainer = VBoxContainer.new()
+ canvas.size_flags_vertical = Control.SIZE_EXPAND_FILL
+ canvas.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ if canvas.get_child_count() < 1:
+ var childs : Array[Node] = control.get_children()
+ for n : Node in childs:
+ control.remove_child(n)
+ canvas.add_child(n)
+
+ canvas.size = control.size
+ mickey = MickeyToolRoute.new(control, canvas, canvas)
+ return mickey
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd.uid
new file mode 100644
index 0000000..78266ad
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/helper_editor_tool.gd.uid
@@ -0,0 +1 @@
+uid://cnor3blarugxa
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd
new file mode 100644
index 0000000..f26e31c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd
@@ -0,0 +1,228 @@
+@tool
+extends RefCounted
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const Notfy = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd")
+
+signal focus(_tool : Object)
+signal new_symbol(symbol : String)
+signal clear()
+
+var _owner : Control = null
+var _root_control : Control = null
+var _control : Control = null
+var _index : int = -1
+
+var _queue_free : bool = false
+
+func set_queue_free(q : bool) -> void:
+ _queue_free = q
+
+func is_queue_free() -> bool:
+ return _queue_free
+
+func is_valid() -> bool:
+ for x : Variant in [_owner, _root_control, _control]:
+ if !is_instance_valid(x) or (x as Node).is_queued_for_deletion() or !(x as Node).is_inside_tree():
+ return false
+ return _owner != get_root()
+
+func update_metadata(tittle : String, tooltips : String, icon : Texture2D) -> void:
+ if is_instance_valid(_control):
+ var parent : Node = _root_control
+ for __ : int in range(0, 4, 1):
+ if parent is TabContainer or parent == null:
+ break
+ parent = parent.get_parent()
+
+ if parent is TabContainer:
+ var index : int = _root_control.get_index()
+ if index > -1 and parent.get_tab_count() > index:
+ if !tittle.is_empty() and parent.get_tab_title(index) != tittle:
+ parent.set_tab_title(index, tittle)
+ _root_control.name = tittle
+ if !tooltips.is_empty() and parent.get_tab_tooltip(index) != tooltips:
+ parent.set_tab_tooltip(index, tooltips)
+ parent.set_tab_icon(index, icon)
+
+func ochorus(root : Node) -> void:
+ if is_instance_valid(_root_control) and is_instance_valid(root):
+ var parent : Node = _root_control.get_parent()
+ if parent != root:
+
+ _connect_callback(false)
+
+ if parent:
+ _root_control.reparent(root)
+ else:
+ root.add_child(_root_control)
+
+ if _owner == root:
+ if _root_control.get_index() != _index:
+ if _owner.get_child_count() > _index:
+ _owner.move_child(_root_control, _index)
+ else:
+ if root is TabContainer:
+ var tittle_id : int = _root_control.get_index()
+ if tittle_id > -1 and tittle_id < root.get_tab_count():
+ var tl : String = root.get_tab_title(tittle_id)
+ if tl.is_empty() or (tl.begins_with("@") and "Text" in tl):
+ root.set_tab_title(tittle_id, "Editor")
+
+ _connect_callback(true)
+
+ _root_control.visible = true
+
+func trigger_focus() -> void:
+ focus.emit(self)
+
+func get_owner() -> Node:
+ return _owner
+
+func get_root() -> Node:
+ if _root_control:
+ return _root_control.get_parent()
+ return null
+
+func get_root_control() -> Node:
+ if _root_control:
+ var node : Node = _root_control.get_parent()
+ if node:
+ return node.get_parent()
+ return null
+
+
+func get_control() -> Node:
+ return _root_control
+
+func get_gui() -> Node:
+ return _control
+
+func has(current_control : Node) -> bool:
+ return _owner == current_control or _root_control == current_control or _control == current_control or get_root() == current_control
+
+func _init(owner_control : Control, current_root_control : Control, current_control : Control) -> void:
+ _owner = owner_control
+ _root_control = current_root_control
+ _control = current_control
+ _index = current_root_control.get_index()
+
+ _owner.tree_exiting.connect(reset)
+
+ for x : Control in [
+ _owner, _root_control, _control
+ ]:
+ x.set_script(Notfy)
+ if _owner == x:
+ x.panic()
+ if x.has_signal(&"notification"):
+ if !x.is_connected(&"notification", _on_not):
+ x.connect(&"notification", _on_not)
+
+ _con_focus(_control, true)
+
+func _con_focus(n : Node, con : bool) -> void:
+ if n is Control:
+ if n.focus_mode != Control.FOCUS_NONE:
+ if con:
+ if !_control.gui_input.is_connected(_on_input):
+ _control.gui_input.connect(_on_input)
+ else:
+ if _control.gui_input.is_connected(_on_input):
+ _control.gui_input.disconnect(_on_input)
+ for x : Node in n.get_children():
+ _con_focus(x, con)
+
+func _get_callables(gui : Control) -> Array:
+ return [
+ [gui.focus_entered, _i_like_coffe],
+ #[gui.focus_exited, _i_like_candy],
+ #[gui.visibility_changed, _i_like_coffe],
+ ]
+
+func _connect_callback(con : bool) -> void:
+ var gui : Control = _control
+ if gui is VBoxContainer:
+ gui = gui.get_child(0)
+
+ var arr : Array = _get_callables(gui)
+
+ if gui is CodeEdit:
+ arr.append([gui.symbol_lookup, _on_symb])
+
+ if _control.focus_mode != Control.FOCUS_NONE:
+ _con_focus(_control, con)
+
+ for x : Array in arr:
+ if con:
+ if !x[0].is_connected(x[1]):
+ x[0].connect(x[1])
+ else:
+ if x[0].is_connected(x[1]):
+ x[0].disconnect(x[1])
+
+ if con:
+ if is_instance_valid(gui):
+ focus.emit.call_deferred(self)
+ elif is_instance_valid(_control):
+ _control.modulate = Color.WHITE
+
+func _on_not(what : int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ reset()
+
+func get_index() -> int:
+ if is_instance_valid(_owner) and _owner.is_inside_tree():
+ return _owner.get_index()
+ return -1
+
+func _i_like_coffe() -> void:
+ focus.emit(self)
+
+func reset() -> void:
+ for x : Variant in [
+ _owner, _root_control, _control
+ ]:
+ if is_instance_valid(x):
+ x.set_script(null)
+
+ if _control is CodeEdit and !_control.is_queued_for_deletion() and _control.get_parent() is VSplitContainer:
+ for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ x.script_merge(_control)
+ break
+
+ ochorus(_owner)
+ set_queue_free(true)
+ _owner = null
+ _root_control = null
+ _control = null
+
+ clear.emit()
+
+func _context_update(window : Window, control : Control) -> void:
+ if is_instance_valid(window) and is_instance_valid(control):
+ var screen_rect: Rect2 = DisplayServer.screen_get_usable_rect(window.current_screen)
+ var gvp: Vector2 = control.get_screen_position() + control.get_local_mouse_position()
+ gvp.y = min(gvp.y, screen_rect.position.y + screen_rect.size.y - window.size.y + 16.0)
+ gvp.x = min(gvp.x, screen_rect.position.x + screen_rect.size.x - window.size.x + 16.0)
+ window.set_deferred(&"position", gvp)
+
+func _on_input(input : InputEvent) -> void:
+ if input is InputEventMouseMotion:
+ return
+
+ if input is InputEventMouseButton:
+ if input.pressed and input.button_index == MOUSE_BUTTON_RIGHT:
+ for x : Node in _owner.get_children():
+ var variant : Node = x
+ if variant is Window and _control is Control:
+ _context_update.call_deferred(variant, _control)
+ trigger_focus()
+
+func _on_symb(symbol: String, _line : int, _column: int, _edit : CodeEdit = null) -> void:
+ new_symbol.emit(symbol)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd.uid
new file mode 100644
index 0000000..0966307
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd.uid
@@ -0,0 +1 @@
+uid://r4j0eu5er1m4
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd
new file mode 100644
index 0000000..2e48e32
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd
@@ -0,0 +1,50 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func has(current_control : Node) -> bool:
+ if super(current_control):
+ return true
+ for x : Node in current_control.get_children():
+ if super(x):
+ return true
+ return false
+
+func ochorus(root : Node) -> void:
+ if is_instance_valid(_root_control) and is_instance_valid(root):
+ var parent : Node = _root_control.get_parent()
+ if parent != root:
+
+ _connect_callback(false)
+
+ if _owner == root:
+ var childs : Array[Node] = _root_control.get_children()
+ for n : Node in childs:
+ _root_control.remove_child(n)
+ _owner.add_child(n)
+ _root_control.queue_free()
+ else:
+ if parent:
+ _root_control.reparent(root)
+ else:
+ root.add_child(_root_control)
+
+ if root is Control:
+ _root_control.size = root.size
+
+ if root is TabContainer:
+ var tittle_id : int = _root_control.get_index()
+ if tittle_id > -1 and tittle_id < root.get_tab_count():
+ var tl : String = root.get_tab_title(tittle_id)
+ if tl.is_empty() or (tl.begins_with("@") and "Text" in tl):
+ root.set_tab_title(tittle_id, "Editor")
+
+ _connect_callback(true)
+
+ _root_control.visible = true
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd.uid
new file mode 100644
index 0000000..fae7083
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/magic/mickey_tool_route.gd.uid
@@ -0,0 +1 @@
+uid://pkj5o6q7sine
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd
new file mode 100644
index 0000000..8302789
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd
@@ -0,0 +1,26 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func _build_tool(control : Node) -> MickeyTool:
+ if control is ScriptEditorBase:
+ var editor : Control = control.get_base_editor()
+ var mickey_tool : MickeyTool = null
+ if editor is CodeEdit:
+ var rcontrol : Node = editor.get_parent()
+ if is_instance_valid(rcontrol):
+ for __ : int in range(5):
+ if rcontrol == null:
+ break
+ elif rcontrol is VSplitContainer:
+ mickey_tool = MickeyTool.new(rcontrol.get_parent(), rcontrol, editor)
+ break
+ rcontrol = rcontrol.get_parent()
+ return mickey_tool
+ return null
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd.uid
new file mode 100644
index 0000000..c2a9cc9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/script_editor_tool.gd.uid
@@ -0,0 +1 @@
+uid://c5mrlc852aghg
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd
new file mode 100644
index 0000000..d3201c5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd
@@ -0,0 +1,22 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/editor_tool.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func _build_tool(control : Node) -> MickeyTool:
+ if control is ScriptEditorBase:
+ var editor : Control = control.get_base_editor()
+ var mickey_tool : MickeyTool = null
+ if editor is CodeEdit:
+ var parent : Node = control.get_parent()
+ if parent != null and parent.is_node_ready() and !control.get_parent() is VSplitContainer:
+ mickey_tool = MickeyTool.new(control, editor, editor)
+ else:
+ mickey_tool = MickeyTool.new(control, editor, editor)
+ return mickey_tool
+ return null
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd.uid
new file mode 100644
index 0000000..fd57ac1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/editor/tools/text_editor_tool.gd.uid
@@ -0,0 +1 @@
+uid://xkgq82knloas
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg
new file mode 100644
index 0000000..4e3a514
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg.import
new file mode 100644
index 0000000..72f8fc9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c042di3o7rqml"
+path="res://.godot/imported/MultiSpliter.svg-6817b55c935448e01c4c56c2cdea2496.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg"
+dest_files=["res://.godot/imported/MultiSpliter.svg-6817b55c935448e01c4c56c2cdea2496.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg
new file mode 100644
index 0000000..28877b8
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg
@@ -0,0 +1,89 @@
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg.import
new file mode 100644
index 0000000..7afcb63
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d1oshw4o6jues"
+path="res://.godot/imported/MultiSpliterButton.svg-c4f3205a91f571beaa6eaa4d00d0c837.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg"
+dest_files=["res://.godot/imported/MultiSpliterButton.svg-c4f3205a91f571beaa6eaa4d00d0c837.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg
new file mode 100644
index 0000000..09251a4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg
@@ -0,0 +1,69 @@
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg.import b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg.import
new file mode 100644
index 0000000..c43a2d3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dvg5cjdqtdpd7"
+path="res://.godot/imported/MultiSpliterItem.svg-d4de4583cb5a7de8e2fcdbebea722e6d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg"
+dest_files=["res://.godot/imported/MultiSpliterItem.svg-d4de4583cb5a7de8e2fcdbebea722e6d.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=8.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd
new file mode 100644
index 0000000..fb1af1a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd
@@ -0,0 +1,998 @@
+@tool
+@icon("icon/MultiSpliter.svg")
+extends Container
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# https://github.com/CodeNameTwister/Multi-Split-Container
+#
+# Multi-Split-Container addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const SplitContainerItem : Script = preload("split_container_item.gd")
+const SplitButton : Texture = preload("icon/MultiSpliterButton.svg")
+
+@export_category("Multi-Split Settings")
+## Max columns by rows, after added childs this is eparated by row group by columns size!
+## [br][br]
+## if this value is 0, will not create rows spliters.
+@export_range(0.0, 1000.0, 1.0) var max_columns : int = 0:
+ set(e):
+ max_columns = maxi(0, e)
+
+ if Engine.is_editor_hint():
+ for x : int in range(separators_line_offsets.size()):
+ separators_line_offsets[x] = 0.0
+ for x : LineSep in _separators:
+ x.queue_free()
+
+ _separators.clear()
+ _first = true
+ update()
+
+@export_group("Line Separator", "separator_line")
+## Line separator size.
+@export var separator_line_size : float = 4.0:
+ set(e):
+ separator_line_size = max(e, 0.0)
+ update()
+
+## Separator line color.
+@export var separator_line_color : Color = Color.MAGENTA:
+ set(e):
+ separator_line_color = e
+ if separator_line_color == Color.MAGENTA: # That color reminds me of texture not found errors.
+ var root = EditorInterface.get_base_control()
+ separator_line_color = root.get_theme_color("base_color", "Editor")
+ update()
+
+## Separator line visibility.
+@export var separator_line_visible : bool = true:
+ set(e):
+ separator_line_visible = e
+ for l : LineSep in _separators:
+ l.visible = separator_line_visible
+
+@export_subgroup("Behaviour", "behaviour_")
+## Enable function for auto expand lines container on inside focus.
+@export var behaviour_expand_on_focus : bool = true
+
+## Enable function for auto expand lines container on double click in the line.
+@export var behaviour_expand_on_double_click : bool = true:
+ set(e):
+ behaviour_expand_on_double_click = e
+ for l : LineSep in _separators:
+ l.double_click_handler = behaviour_expand_on_double_click
+
+## Enable movement by touching line.
+@export var behaviour_can_move_by_line : bool = true:
+ set(e):
+ behaviour_can_move_by_line = e
+ for l : LineSep in _separators:
+ l.draggable = behaviour_can_move_by_line
+
+## This allow expand you current focused container if you shrunk it.
+@export var behaviour_can_expand_focus_same_container : bool = false
+
+## Enable smooth when expand container.
+@export var behaviour_expand_smoothed : bool = true:
+ set(e):
+ behaviour_expand_smoothed = e
+ if !e:
+ if _tween and _tween.is_running():
+ _tween.kill()
+ _tween = null
+
+## Time speed duration for reset expand container.
+@export_range(0.01, 1000.0, 0.01) var behaviour_expand_smoothed_time : float = 0.24:
+ set(e):
+ behaviour_expand_smoothed_time = maxf(0.01, e)
+ if _tween and _tween.is_running():
+ _tween.kill()
+ _tween = null
+
+## Custom initial offset for separator lines. (TODO: Still Working here!)
+@export var separators_line_offsets : Array[float] :
+ set(e):
+ separators_line_offsets = e
+
+ if Engine.is_editor_hint():
+ if separators_line_offsets.size() != _separators.size():
+ separators_line_offsets.resize(_separators.size())
+ update()
+
+@export_subgroup("Drag Button", "drag_button")
+
+## Set if drag button always be visible (Useful for test button size)
+@export var drag_button_always_visible : bool = false:
+ set(e):
+ drag_button_always_visible = e
+
+ var min_visible_drag_button : float = 0.0
+ if drag_button_always_visible:
+ min_visible_drag_button = 0.4
+
+ for l : LineSep in _separators:
+ if l.button:
+ l.button.modulate.a = 0.0
+ l.button.min_no_focus_transparense = min_visible_drag_button
+
+## Min size for drag button visible on split lines.
+@export_range(1.0, 200.0, 0.1) var drag_button_size : float = 24.0:
+ set(e):
+ drag_button_size = e
+ update()
+
+## Modulate color for the drag button.
+@export var drag_button_modulate : Color = Color.MAGENTA:
+ set(e):
+ drag_button_modulate = e
+ if drag_button_modulate == Color.MAGENTA:
+ if Engine.is_editor_hint():
+ var root : Control = EditorInterface.get_base_control()
+ drag_button_modulate = root.get_theme_color("base_color", "Editor").lightened(0.5)
+ update()
+
+## Change default drag button icon.
+@export var drag_button_icon : Texture = null:
+ set(e):
+ drag_button_icon = e
+ update()
+
+var _separators : Array[LineSep] = []
+var _last_container_focus : Node = null
+var _frame : int = 1
+var _first : bool = true
+var _tween : Tween = null
+
+func get_separators() -> Array[LineSep]:
+ return _separators
+
+## Get line begin offset limit.
+func get_line_seperator_left_offset_limit(index : int) -> float:
+ if index < _separators.size():
+ var line_sep : LineSep = _separators[index]
+ if !line_sep.is_vertical:
+ if index < 1:
+ return -_separators[index].initial_position.x
+ var next : LineSep = _separators[index - 1]
+ return (next.initial_position.x + (next.size.x/2.0)) - _separators[index].initial_position.x
+ else:
+ if index < 1:
+ return -_separators[index].initial_position.y
+ var next : LineSep = _separators[index - 1]
+ return (next.initial_position.y + (next.size.y/2.0)) - _separators[index].initial_position.y
+ push_warning("[PLUGIN] Not valid index for line separator!")
+ return 0.0
+
+## Get line end offset limit.
+func get_line_seperator_right_offset_limit(index : int) -> float:
+ if index < _separators.size():
+ var line_sep : LineSep = _separators[index]
+ if !line_sep.is_vertical:
+ if index + 1 == _separators.size():
+ return (size.x/2.0) -_separators[index].initial_position.x
+ var current : LineSep = _separators[index]
+ return (_separators[index + 1].initial_position.x - current.initial_position.x + (current.size.x/2.0))
+ else:
+ if index + 1 == _separators.size():
+ return size.x -_separators[index].initial_position.y
+ var current : LineSep = _separators[index]
+ return (_separators[index + 1].initial_position.y - current.initial_position.y + (current.size.y/2.0))
+ push_warning("[PLUGIN] Not valid index for line separator!")
+ return 0.0
+
+# This is function is util when you want expand or constraint manualy offset.
+## Update offset of the line
+func update_line_separator_offset(index : int, offset : float) -> void:
+ var line_sep : LineSep = _separators[index]
+ line_sep.offset = offset
+ line_sep.force_update()
+
+## Get total line count.
+func get_line_separator_count() -> int:
+ return _separators.size()
+
+## Get Line reference by index, see get_line_separator_count()
+func get_line_separator(index : int) -> LineSep:
+ return _separators[index]
+
+## Get if line separator is vertical.
+func is_vertical_line_separator(index : int) -> bool:
+ if index < _separators.size():
+ return _separators[index].is_vertical
+ push_warning("[PLUGIN] Not valid index for line separator!")
+ return false
+
+## Expand splited container by index container.
+func expand_splited_container(node : Node) -> void:
+ var same : bool = _last_container_focus == node
+ if same and !behaviour_can_expand_focus_same_container:
+ return
+
+ _last_container_focus = node
+
+ if !behaviour_expand_on_focus:
+ return
+
+ if _tween and _tween.is_running():
+ if same:
+ return
+ _tween.kill()
+ _tween = null
+
+ var top_lines : Array[LineSep] = []
+ var bottom_lines : Array[LineSep] = []
+
+ var update_required : bool = false
+
+ for line : LineSep in _separators:
+ if node in line.top_items:
+ update_required = update_required or line.offset < 0.0
+ top_lines.append(line)
+ elif node in line.bottom_items:
+ update_required = update_required or line.offset > 0.0
+ bottom_lines.append(line)
+
+ if update_required:
+ if behaviour_expand_smoothed:
+ _tween = get_tree().create_tween()
+ _tween.tween_method(_reset_expanded_lines.bind(top_lines, bottom_lines), 0.0, 1.0, behaviour_expand_smoothed_time)
+ else:
+ _reset_expanded_lines(1.0, top_lines, bottom_lines)
+
+func _reset_expanded_lines(_lerp : float, top_lines : Array[LineSep], bottom_lines : Array[LineSep]) -> void:
+ for iline : int in range(top_lines.size() - 1, -1, -1):
+ var line : LineSep = top_lines[iline]
+ if is_instance_valid(line):
+ if line.offset < 0.0:
+ line.offset = lerp(line.offset, 0.0, _lerp)
+ else:
+ top_lines.remove_at(iline)
+
+ for iline : int in range(bottom_lines.size() - 1, -1, -1):
+ var line : LineSep = bottom_lines[iline]
+ if is_instance_valid(line):
+ if line.offset > 0.0:
+ line.offset = lerp(line.offset, 0.0, _lerp)
+ else:
+ bottom_lines.remove_at(iline)
+
+ for line : LineSep in top_lines:
+ line.force_update()
+ for line : LineSep in bottom_lines:
+ line.force_update()
+
+## Get initial position of a separator line.
+func get_line_separator_initial_position(index : int) -> Vector2:
+ if index < _separators.size():
+ return _separators[index].initial_position
+ push_warning("[PLUGIN] Not valid index for line separator!")
+ return Vector2.ZERO
+
+class DragButton extends Button:
+ var _frm : float = 0.0
+ var _line_sep : LineSep = null
+ var _is_pressed : bool = false
+
+ var is_hover : bool = false
+
+ var _hover : Array[bool] = [false, false]
+
+ var min_no_focus_transparense : float = 0.0:
+ set(e):
+ min_no_focus_transparense = e
+ modulate.a = maxf(modulate.a, min_no_focus_transparense)
+
+ static var DEFAULT_STYLE : StyleBox = null
+
+ func set_drag_icon(new_icon : Texture) -> void:
+ if icon != new_icon:
+ if new_icon == null:
+ icon = SplitButton
+ return
+ icon = new_icon
+
+ func update_gui() -> void:
+ if !_line_sep:
+ return
+
+ if _line_sep.is_vertical:
+ _line_sep.mouse_default_cursor_shape = Control.CURSOR_VSPLIT
+ mouse_default_cursor_shape = Control.CURSOR_VSPLIT
+ else:
+ _line_sep.mouse_default_cursor_shape = Control.CURSOR_HSPLIT
+ mouse_default_cursor_shape = Control.CURSOR_HSPLIT
+
+ func set_line(line_sep : LineSep) -> void:
+ if _line_sep:
+ if _line_sep.mouse_entered.is_connected(_on_enter):
+ _line_sep.mouse_entered.disconnect(_on_enter)
+ if _line_sep.mouse_exited.is_connected(_on_exit):
+ _line_sep.mouse_exited.disconnect(_on_exit)
+ if _line_sep.gui_input.is_connected(_on_input):
+ _line_sep.gui_input.disconnect(_on_input)
+
+ _line_sep = line_sep
+
+ if _line_sep:
+ if !_line_sep.mouse_entered.is_connected(_on_enter):
+ _line_sep.mouse_entered.connect(_on_enter.bind(1))
+ if !_line_sep.mouse_exited.is_connected(_on_exit):
+ _line_sep.mouse_exited.connect(_on_exit.bind(1))
+ if !_line_sep.gui_input.is_connected(_on_input):
+ _line_sep.gui_input.connect(_on_input)
+
+
+ func _init(line_sep : LineSep = null) -> void:
+ modulate.a = 0.0
+
+ set_line(line_sep)
+
+ button_down.connect(_on_press)
+ button_up.connect(_out_press)
+ mouse_entered.connect(_on_enter.bind(0))
+ mouse_exited.connect(_on_exit.bind(0))
+
+ gui_input.connect(_custom_input)
+
+ icon = SplitButton
+ icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
+ expand_icon = true
+
+ if null != icon:
+ flat = true
+
+ if DEFAULT_STYLE == null:
+ DEFAULT_STYLE = StyleBoxEmpty.new()
+
+ focus_mode = Control.FOCUS_CLICK
+
+ set(&"theme_override_styles/focus", DEFAULT_STYLE)
+ set(&"theme_override_styles/disabled_mirrored", DEFAULT_STYLE)
+ set(&"theme_override_styles/disabled", DEFAULT_STYLE)
+ set(&"theme_override_styles/hover_pressed_mirrored", DEFAULT_STYLE)
+ set(&"theme_override_styles/hover_pressed", DEFAULT_STYLE)
+ set(&"theme_override_styles/hover_mirrored", DEFAULT_STYLE)
+ set(&"theme_override_styles/hover", DEFAULT_STYLE)
+ set(&"theme_override_styles/pressed_mirrored", DEFAULT_STYLE)
+ set(&"theme_override_styles/pressed", DEFAULT_STYLE)
+ set(&"theme_override_styles/normal_mirrored", DEFAULT_STYLE)
+ set(&"theme_override_styles/normal", DEFAULT_STYLE)
+
+ z_as_relative = true
+ z_index = 20
+
+ update_gui()
+
+ func _custom_input(e : InputEvent) -> void:
+ if e is InputEventMouseButton:
+ if e.pressed and e.double_click:
+ get_tree().call_group(&"ScriptSplitter", &"swap", get_parent())
+
+ func _on_input(e : InputEvent) -> void:
+ if e is InputEventMouseButton:
+ if e.pressed and e.double_click:
+ if _line_sep and _line_sep.double_click_handler:
+ _line_sep.offset = 0.0
+ _line_sep.offset_updated.emit()
+ elif e.pressed and _line_sep.draggable and e.button_index == 1:
+ button_down.emit()
+ elif !e.pressed and _line_sep.draggable and e.button_index == 1:
+ button_up.emit()
+
+ func set_line_sep_reference(ref : LineSep) -> void:
+ _line_sep = ref
+
+ func _ready() -> void:
+ set_process(false)
+
+ func _on_enter(x : int = 0) -> void:
+ _hover[x] = true
+
+ _frm = 0.0
+ modulate.a = 1.0
+ is_hover = true
+ set_process(true)
+
+ func _on_exit(x : int = 0) -> void:
+ _hover[x] = false
+
+ for h : bool in _hover:
+ if h != false:
+ return
+
+ _frm = 0.0
+ modulate.a = 1.0
+ is_hover = false
+ set_process(true)
+
+ func _on_press() -> void:
+ _is_pressed = true
+ _frm = 0.0
+ modulate.a = 1.0
+ set_process(true)
+
+ func _out_press() -> void:
+ _is_pressed = false
+ set_process(true)
+
+ func _process(delta : float) -> void:
+ if !has_focus() and !is_hover:
+ _frm += delta * 0.4
+ if _frm >= 1.0:
+ _frm = 1.0
+ set_process(false)
+ modulate.a = lerp(modulate.a, min_no_focus_transparense, _frm)
+ if _is_pressed:
+ var mpos : Vector2 = _line_sep.get_parent().get_local_mouse_position()
+ if mpos != get_rect().get_center():
+ _line_sep.update_offset_by_position(mpos)
+
+class UndoredoSplit extends RefCounted:
+ var object : SplitContainerItem = null
+ var c_objects : Array[Node] = []
+
+class LineSep extends ColorRect:
+ signal offset_updated()
+
+ var top_lines : Array[LineSep] = []
+ var bottom_lines : Array[LineSep] = []
+
+ var top_items : Array[Control] = []
+ var bottom_items : Array[Control] = []
+
+ var is_vertical : bool = false:
+ set(e):
+ is_vertical = e
+
+ if button:
+ button.update_gui()
+
+ var row : int = 0
+
+ var initial_position : Vector2 = Vector2.ZERO
+ var offset : float = 0.0
+
+ var min_size_offset : float = 0.0
+
+ var prev_line : LineSep = null
+ var next_line : LineSep = null
+
+ var button : DragButton = null
+
+ var double_click_handler : bool = true
+ var draggable : bool = true
+
+ func set_next_line(next : LineSep = null) -> void:
+ next_line = next
+ next.prev_line = self
+
+ func clear() -> void:
+ top_items.clear()
+ bottom_items.clear()
+ top_lines.clear()
+ bottom_lines.clear()
+
+ func reset() -> void:
+ position = initial_position
+ update_items()
+
+ func update_items() -> void:
+ if is_vertical:
+ for item : Control in top_items:
+ item.size.y = position.y - item.position.y
+ if !prev_line:
+ item.position.y = 0.0
+
+ for item : Control in bottom_items:
+ item.position.y = position.y + size.y
+
+ if next_line:
+ item.size.y = next_line.position.y - item.position.y
+ else:
+ item.size.y = get_parent().size.y - item.position.y
+ else:
+ for item : Control in top_items:
+ item.size.x = position.x - item.position.x + (size.x / 2.0) - 2.0
+ if !prev_line:
+ item.position.x = 0.0
+
+ for item : Control in bottom_items:
+ var diff : float = position.x + (size.x / 2.0) + 2.0
+ item.position.x = diff
+
+ if next_line:
+ item.size.x = next_line.position.x - item.position.x
+ else:
+ item.size.x = get_parent().size.x - item.position.x
+
+ func force_update() -> void:
+ update_offset_by_position(initial_position + Vector2(offset * int(!is_vertical), offset * int(is_vertical)))
+
+ func get_current_position() -> Vector2:
+ return initial_position + Vector2(offset * int(!is_vertical), offset * int(is_vertical))
+
+ func update_offset_by_position(vpos : Vector2) -> void:
+ if is_vertical:
+ min_size_offset = 0.0
+ for x : Control in top_items:
+ min_size_offset = maxf(min_size_offset, x.get_minimum_size().y)
+ if prev_line:
+ prev_line.min_size_offset = 0.0
+ for x : Control in prev_line.bottom_items:
+ prev_line.min_size_offset = maxf(prev_line.min_size_offset, x.get_minimum_size().y)
+
+ offset = vpos.y - initial_position.y
+ offset = minf(offset, get_parent().size.y - (initial_position.y + size.y + min_size_offset))
+ offset = maxf(offset, -(initial_position.y - min_size_offset))
+
+ if next_line:
+ var val : float = next_line.position.y - (initial_position.y + size.y + min_size_offset)
+ if offset > val:
+ offset = val
+ else:
+ var val : float = get_parent().size.y - (initial_position.y + (size.y / 2.0) + min_size_offset)
+ if offset > val:
+ offset = val
+ if prev_line:
+ var val : float = -(initial_position.y - (prev_line.position.y + prev_line.size.y + prev_line.min_size_offset))
+
+ if offset < val:
+ offset = val
+ else:
+ var top_size_offset : float = 0.0
+ for x : Control in top_items:
+ top_size_offset = maxf(top_size_offset, x.get_minimum_size().y)
+ offset = maxf(offset, top_size_offset-initial_position.y)
+
+ position.y = initial_position.y + offset
+
+ for line : LineSep in top_lines:
+ line.size.y = position.y - line.position.y
+
+ for line : LineSep in bottom_lines:
+ line.position.y = position.y + size.y
+
+ if next_line:
+ line.size.y = next_line.position.y - line.position.y
+ else:
+ line.size.y = get_parent().size.y - line.position.y
+ else:
+ min_size_offset = 0.0
+ for x : Control in bottom_items:
+ min_size_offset = maxf(min_size_offset, x.get_minimum_size().x)
+
+ if prev_line:
+ prev_line.min_size_offset = 0.0
+ for x : Control in prev_line.bottom_items:
+ prev_line.min_size_offset = maxf(prev_line.min_size_offset, x.get_minimum_size().x)
+
+ offset = vpos.x - initial_position.x
+ offset = minf(offset, get_parent().size.x - (initial_position.x + size.x + min_size_offset))
+ offset = maxf(offset, -initial_position.x)
+
+ if next_line:
+ var val : float = next_line.position.x - (initial_position.x + size.x + min_size_offset)
+ if offset > val:
+ offset = val
+ else:
+ var val : float = get_parent().size.x - (initial_position.x + (size.x/2.0) + min_size_offset)
+ if offset > val:
+ offset = val
+ if prev_line:
+ var val : float = -(initial_position.x - (prev_line.position.x + prev_line.size.x + prev_line.min_size_offset))
+
+ if offset < val:
+ offset = val
+ else:
+ var top_size_offset : float = 0.0
+ for x : Control in top_items:
+ top_size_offset = maxf(top_size_offset, x.get_minimum_size().x)
+ offset = maxf(offset, top_size_offset-initial_position.x)
+
+ position.x = initial_position.x + offset
+ update_items()
+
+ func _draw() -> void:
+ update()
+
+ func update() -> void:
+ button.rotation_degrees = 90.0 * int(is_vertical)
+ button.pivot_offset = button.size / 2.0
+ button.position = size / 2.0 - button.pivot_offset
+
+
+
+ func _init() -> void:
+ color = Color.RED
+
+ func _ready() -> void:
+ name = "SplitLine"
+ if button == null:
+ button = DragButton.new(self)
+ add_child(button, false, Node.INTERNAL_MODE_BACK)
+
+func _test() -> void:
+ queue_redraw()
+
+func _init() -> void:
+ child_entered_tree.connect(_on_enter)
+ child_exiting_tree.connect(_on_exiting)
+
+func update() -> void:
+ set_process(true)
+
+func _create_separator() -> Control:
+ var line_sep : LineSep = LineSep.new()
+ line_sep.offset_updated.connect(update)
+ return line_sep
+
+func _undoredo_undo(ur : UndoredoSplit) -> void:
+ if !is_instance_valid(ur):
+ return
+
+ var split : SplitContainerItem = ur.object
+ if is_instance_valid(split):
+ if split.get_parent() == self:
+ ur.c_objects = split.get_children()
+ for x : Node in ur.c_objects:
+ split.remove_child(x)
+ if x is Control:
+ x.visible = false
+ add_child(x)
+ if is_instance_valid(split) and split.get_parent() == self:
+ remove_child(split)
+
+func _update() -> void:
+ var items : Array[Control] = []
+ for x : Node in get_children():
+ if is_instance_valid(x) and x is Control:
+ if x.visible and !x.is_queued_for_deletion():
+ if x is SplitContainerItem:
+ if x.get_child_count() > 0:
+ var _is_visible : bool = false
+ for y : Node in x.get_children():
+ if y is Control and y.visible:
+ _is_visible = true
+ break
+ if !_is_visible:
+ continue
+ else:
+ x.queue_free()
+ continue
+ elif x is DragButton or x is LineSep:
+ x.queue_free()
+ continue
+ else:
+ var container : SplitContainerItem = SplitContainerItem.new()
+
+ add_child(container, true)
+
+ x.reparent(container)
+ x = container
+
+
+ x.size_flags_horizontal = Control.SIZE_FILL
+ x.size_flags_vertical = Control.SIZE_FILL
+ x.clip_contents = true
+ x.custom_minimum_size = Vector2.ZERO
+ items.append(x)
+
+ var totals : int = items.size()
+ var rows : int = 0
+
+ if max_columns > 0:
+ var _totals : int = totals
+ rows = 0
+ while _totals > max_columns:
+ _totals -= max_columns
+ rows += 1
+ totals -= rows
+
+ if totals < 1:
+ for x : int in range(0, _separators.size(), 1):
+ _separators[x].queue_free()
+ _separators[x] = null
+ _separators.clear()
+
+ for x : Control in items:
+ x.position = Vector2.ZERO
+ x.size = get_rect().size
+ return
+ else:
+ if separator_line_size <= 0.0:
+ for x : int in range(0, _separators.size(), 1):
+ _separators[x].queue_free()
+ _separators[x] = null
+ _separators.clear()
+ else:
+ var sep : int = totals - 1 + rows
+ for x : int in range(sep, _separators.size(), 1):
+ _separators[x].queue_free()
+ _separators[x] = null
+ _separators.resize(sep)
+ for x : int in range(0, _separators.size(), 1):
+ if _separators[x] == null:
+ _separators[x] = _create_separator()
+
+ rows += 1
+ if max_columns > 1:
+ if totals > max_columns:
+ totals = max_columns
+
+ var rect_size : Vector2 = get_rect().size
+ var start_position : Vector2 = Vector2.ZERO
+
+ var size_split : Vector2 = (rect_size / Vector2(totals, rows))
+
+ var size_sep : Vector2 = Vector2.ONE * separator_line_size
+
+ if totals > 1:
+ size_sep = (size_sep / (totals - 1))
+
+ var item_size : Vector2 = Vector2(size_split.x, size_split.y)
+ var line_size : Vector2 = Vector2(separator_line_size, item_size.y)
+
+ var total_items : int = items.size()
+
+ var vpos : Vector2 = Vector2.ZERO
+ var current_row : int = 0
+
+ var item_index : int = 0
+
+ var last_vline : LineSep = null
+ var last_hline : LineSep = null
+
+ for x : Control in items:
+ x.position = Vector2.ZERO
+ x.size = x.get_minimum_size()
+
+ for z : int in range(_separators.size()):
+ var x : LineSep = _separators[z]
+
+ x.clear()
+
+ start_position.x += 1
+
+ if 0 < max_columns and start_position.x + 1 > max_columns:
+ total_items -= max_columns
+ start_position.x = 0.0
+ start_position.y += 1.0
+ current_row += 1
+ if total_items <= max_columns and total_items > 0:
+ size_split = (rect_size / Vector2(total_items, rows))
+ if total_items == 1:
+ size_sep = Vector2.ONE * separator_line_size
+ else:
+ size_sep = ((Vector2.ONE * separator_line_size) / (total_items - 1))
+ item_size = Vector2(size_split.x, size_split.y)
+ line_size = Vector2(separator_line_size, rect_size.y - x.position.y)
+
+ vpos = Vector2(0.0, start_position.y) * item_size
+ x.is_vertical = true
+
+ if x.get_parent() == null:
+ add_child(x, false, Node.INTERNAL_MODE_BACK)
+
+
+ x.row = current_row
+
+ if items.size() > 0:
+ var it : int = mini(item_index, items.size() - 1)
+ var min_size : float = 0.0
+
+ var _has : bool = false
+
+ for y : int in range(z - 1, -1, -1):
+ if it > -1:
+ var item : Control = items[it]
+ x.top_items.append(item)
+ min_size = maxf(item.get_minimum_size().y, min_size)
+ it -= 1
+
+ var ln : LineSep = _separators[y]
+ if ln.is_vertical:
+ _has = true
+ break
+ x.top_lines.append(ln)
+ if !_has:
+ for _it : int in range(it, -1, -1):
+ var item : Control = items[it]
+ x.top_items.append(item)
+
+ if item_index + 1 < items.size():
+ it = item_index + 1
+ _has = false
+ for y : int in range(z + 1, _separators.size(), 1):
+ if it < items.size():
+ var item : Control = items[it]
+ x.bottom_items.append(item)
+ it += 1
+
+ var ln : LineSep = _separators[y]
+ if ln.is_vertical:
+ _has = true
+ break
+ x.bottom_lines.append(ln)
+ if !_has:
+ for _it : int in range(it, items.size(), 1):
+ var item : Control = items[_it]
+ x.bottom_items.append(item)
+
+ var vline_size : Vector2 = Vector2(rect_size.x, separator_line_size)
+
+ x.initial_position = vpos
+ x.initial_position.y -= (vline_size.y) / 2.0
+ x.position = x.initial_position
+
+ x.button.size = Vector2(drag_button_size, drag_button_size)
+
+ x.set(&"size", vline_size)
+ x.update()
+
+ if last_vline:
+ last_vline.set_next_line(x)
+
+ last_vline = x
+ last_hline = null
+ item_index += 1
+ continue
+
+ vpos = start_position * item_size
+
+ if x.get_parent() == null:
+ add_child(x, false, Node.INTERNAL_MODE_BACK)
+
+
+ if item_index < items.size():
+ var item : Control = items[item_index]
+ x.top_items.append(item)
+ item_index += 1
+ if item_index < items.size():
+ if z + 1 < _separators.size():
+ if !_separators[z].is_vertical:
+ x.bottom_items.append(items[item_index])
+ else:
+ x.bottom_items.append(items[item_index])
+
+ x.initial_position = vpos
+ x.initial_position.x -= (line_size.x) / 2.0
+
+ x.button.size = Vector2(drag_button_size, drag_button_size)
+
+ x.row = current_row
+ x.position = x.initial_position
+
+ x.set(&"size", line_size)
+ x.update()
+
+ if last_hline:
+ last_hline.set_next_line(x)
+ last_hline = x
+
+ for x : Control in items:
+ x.size = size
+
+ var min_visible_drag_button : float = 0.0
+ if drag_button_always_visible:
+ min_visible_drag_button = 0.4
+
+ if _first:
+ for l : LineSep in _separators:
+ l.visible = separator_line_visible
+ l.color = separator_line_color
+ l.double_click_handler = behaviour_expand_on_double_click
+ l.button.self_modulate = drag_button_modulate
+ l.button.min_no_focus_transparense = min_visible_drag_button
+ l.button.set_drag_icon(drag_button_icon)
+ l.draggable = behaviour_can_move_by_line
+
+ l.reset()
+
+ else:
+ if separators_line_offsets.size() > 0:
+ for l : int in range(0, _separators.size(), 1):
+ if l < separators_line_offsets.size():
+ _separators[l].offset = separators_line_offsets[l]
+ continue
+ break
+
+ for l : LineSep in _separators:
+ l.visible = separator_line_visible
+ l.color = separator_line_color
+ l.double_click_handler = behaviour_expand_on_double_click
+ l.button.self_modulate = drag_button_modulate
+ l.button.min_no_focus_transparense = min_visible_drag_button
+ l.button.set_drag_icon(drag_button_icon)
+ l.draggable = behaviour_can_move_by_line
+
+ l.force_update()
+
+ if !Engine.is_editor_hint():
+ separators_line_offsets.clear()
+ else:
+ for l : int in range(0, _separators.size(), 1):
+ if l < separators_line_offsets.size():
+ separators_line_offsets[l] = _separators[l].offset
+ continue
+ break
+
+func _on_enter(n : Node) -> void:
+ n.is_inside_tree()
+ if n is SplitContainerItem or (n is Control and !Engine.is_editor_hint()):
+ if !n.visibility_changed.is_connected(_on_visible):
+ n.visibility_changed.connect(_on_visible)
+ if is_node_ready():
+ for x : int in range(separators_line_offsets.size()):
+ separators_line_offsets[x] = 0.0
+ update()
+
+func _on_visible() -> void:
+ update()
+
+func _on_exiting(n : Node) -> void:
+ if n is SplitContainerItem or (n is Control and !Engine.is_editor_hint()):
+ if is_node_ready():
+ for x : int in range(separators_line_offsets.size()):
+ separators_line_offsets[x] = 0.0
+ for x : LineSep in _separators:
+ x.offset = 0.0
+ if n.visibility_changed.is_connected(_on_visible):
+ n.visibility_changed.disconnect(_on_visible)
+ update()
+
+func _process(__ : float) -> void:
+ if is_node_ready():
+ if _frame > 0:
+ _frame -= 1
+ return
+ _update()
+ if _first:
+ _first = false
+ else:
+ set_process(false)
+
+func _on_exiting_tree() -> void:
+ var vp : Viewport = get_viewport()
+ if vp and vp.size_changed.is_connected(update):
+ vp.size_changed.disconnect(update)
+
+ var parent : Node = get_parent()
+ if parent is Control:
+ if parent.item_rect_changed.is_connected(update):
+ parent.item_rect_changed.disconnect(update)
+
+func _enter_tree() -> void:
+ var vp : Viewport = get_viewport()
+ if vp and !vp.size_changed.is_connected(update):
+ vp.size_changed.connect(update)
+
+ var parent : Node = get_parent()
+ if parent is Control:
+ if !parent.item_rect_changed.is_connected(update):
+ parent.item_rect_changed.connect(update)
+
+ if !tree_exiting.is_connected(_on_exiting_tree):
+ tree_exiting.connect(_on_exiting_tree)
+
+func _on_draw() -> void:
+ update()
+
+func _ready() -> void:
+ separator_line_color = separator_line_color
+ drag_button_modulate = drag_button_modulate
+
+ size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ size_flags_vertical =Control.SIZE_EXPAND_FILL
+
+ set_deferred(&"anchor_left", 0.0)
+ set_deferred(&"anchor_top", 0.0)
+ set_deferred(&"anchor_bottom", 1.0)
+ set_deferred(&"anchor_right", 1.0)
+
+ if Engine.is_editor_hint():
+ draw.connect(_on_draw)
+
+ if _first:
+ update()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd.uid
new file mode 100644
index 0000000..0b4003b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd.uid
@@ -0,0 +1 @@
+uid://sffqwro3flkc
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd
new file mode 100644
index 0000000..223b613
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd
@@ -0,0 +1,69 @@
+@tool
+@icon("icon/MultiSpliterItem.svg")
+extends Control
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# https://github.com/CodeNameTwister/Multi-Split-Container
+#
+# Multi-Split-Container addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+var focus_handler : bool = false
+
+## Expand if tight by spliter
+func show_splited_container() -> void:
+ var parent : Node = get_parent()
+ if parent.has_method(&"expand_splited_container"):
+ parent.call(&"expand_splited_container", self)
+
+
+func _ready() -> void:
+ set_process(false)
+
+ size_flags_horizontal = Control.SIZE_FILL
+ size_flags_vertical = Control.SIZE_FILL
+
+ set_deferred(&"anchor_left", 0.0)
+ set_deferred(&"anchor_top", 0.0)
+ set_deferred(&"anchor_bottom", 1.0)
+ set_deferred(&"anchor_right", 1.0)
+
+func _init() -> void:
+ name = "SplitContainerItem"
+
+ child_exiting_tree.connect(_on_child_exiting_tree)
+ child_entered_tree.connect(_on_child_entered_tree)
+
+func _on_visible() -> void:
+ var _visible : bool = false
+ for x : Node in get_children():
+ if x is Control:
+ if x.visible:
+ _visible = true
+ break
+ visible = _visible
+
+func _on_child_entered_tree(n : Node) -> void:
+ if n is Control:
+ n.size = size
+ n.set_anchor(SIDE_LEFT, 0.0)
+ n.set_anchor(SIDE_RIGHT, 1.0)
+ n.set_anchor(SIDE_TOP, 0.0)
+ n.set_anchor(SIDE_BOTTOM, 1.0)
+ if !n.visibility_changed.is_connected(_on_visible):
+ n.visibility_changed.connect(_on_visible)
+
+func _disconnect(n : Node) -> void:
+ if n is Control:
+ if n.visibility_changed.is_connected(_on_visible):
+ n.visibility_changed.disconnect(_on_visible)
+ for x : Node in n.get_children():
+ _disconnect(x)
+
+func _on_child_exiting_tree(n : Node) -> void:
+ _disconnect(n)
+
+func _enter_tree() -> void:
+ var c : Node = get_parent()
+ if c is Control:
+ size = c.size
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd.uid
new file mode 100644
index 0000000..0596a8d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd.uid
@@ -0,0 +1 @@
+uid://rh6vq7m1qfkr
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/changes.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/changes.gd
new file mode 100644
index 0000000..9a77ef2
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/changes.gd
@@ -0,0 +1,9 @@
+@tool
+extends Label
+
+func _gui_input(event: InputEvent) -> void:
+ if event is InputEventMouseButton:
+ if event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
+ var btn : Node = get_parent().get_child(0)
+ if btn is Button:
+ btn.pressed.emit()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/changes.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/changes.gd.uid
new file mode 100644
index 0000000..7022c81
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/changes.gd.uid
@@ -0,0 +1 @@
+uid://bkj4lec06udg5
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.gd
new file mode 100644
index 0000000..e0c061e
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.gd
@@ -0,0 +1,353 @@
+@tool
+extends PanelContainer
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const TAB = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.tscn")
+
+@export var container : Control = null
+var _dlt : float = 0.0
+var _try : int = 0
+
+var buttons : Array[Control] = []
+var hbox : Array[HBoxContainer] = []
+var pins : PackedStringArray = []
+
+var _enable_update : bool = true
+
+var _reference : TabBar = null
+
+var _select_color : Color = Color.CADET_BLUE:
+ set = set_select_color
+
+var _updating : bool = false
+
+var style : StyleBox = null
+
+func _enter_tree() -> void:
+ modulate.a = 0.0
+ z_index = 10
+ get_tree().create_tween().tween_property(self, "modulate:a", 1.0, 0.3)
+
+ _setup()
+
+func _exit_tree() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+func _on_change() -> void:
+ var dt : Array = [
+ "plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/selected_color"
+ ]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ break
+
+func _setup() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["_select_color", "plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/selected_color"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ for x : Variant in hbox:
+ if is_instance_valid(x):
+ if x.get_parent() == null:
+ x.queue_free()
+ for x : Variant in buttons:
+ if is_instance_valid(x):
+ if x.get_parent() == null:
+ x.queue_free()
+
+func _on_pressed(btn : Button) -> void:
+ if is_instance_valid(_reference):
+ for x : int in _reference.tab_count:
+ if _reference.get_tab_tooltip(x) == btn.tooltip_text:
+ _reference.current_tab = x
+ #_reference.tab_clicked.emit(x)
+ _reference.tab_clicked.emit(x)
+
+func _on_gui_pressed(input : InputEvent, btn : Button) -> void:
+ if input.is_pressed():
+ if is_instance_valid(_reference):
+ for x : int in _reference.tab_count:
+ if _reference.get_tab_tooltip(x) == btn.tooltip_text:
+ if input is InputEventMouseButton:
+ if input.button_index == MOUSE_BUTTON_RIGHT:
+ _reference.tab_selected.emit(x)
+ _reference.tab_rmb_clicked.emit(x)
+ return
+ elif input.button_index == MOUSE_BUTTON_MIDDLE:
+ _reference.tab_close_pressed.emit(x)
+ return
+
+func remove_tab(tooltip : String) -> void:
+ for x : Control in buttons:
+ if x.get_src() == tooltip:
+ x.queue_free()
+ return
+
+func rename_tab(_tab_name : String, tooltip : String, new_tab_name : String, new_tooltip : String) -> void:
+ for x : Button in buttons:
+ if x.get_src() == tooltip:
+ x.set_src(new_tooltip)
+ x.set_text(new_tab_name)
+ return
+
+func set_select_color(color : Color) -> void:
+ _select_color = color.lightened(0.4)
+
+func set_ref(tab : TabBar) -> void:
+ _reference = tab
+ update()
+
+func set_enable(e : bool) -> void:
+ _enable_update = e
+ visible = e
+ if e:
+ _updating = false
+ update()
+ return
+ for x : Variant in hbox:
+ if is_instance_valid(x):
+ x.queue_free()
+ for x : Variant in buttons:
+ if is_instance_valid(x):
+ x.queue_free()
+ buttons.clear()
+ hbox.clear()
+
+func _on_pin(btn : Object) -> void:
+ if btn:
+ if btn.has_method(&"get_src"):
+ var value : Variant = btn.call(&"get_src")
+ if value is String:
+ if value.is_empty():
+ return
+ var x : int = pins.find(value)
+ if x > -1:
+ pins.remove_at(x)
+ else:
+ pins.append(value)
+
+ if pins.size() > 30:
+ var exist : Dictionary[String, bool] = {}
+ for b : Button in buttons:
+ exist[b.tooltip_text] = true
+
+ for y : int in range(pins.size() - 1, -1, -1):
+ if !exist.has(pins[y]):
+ pins.remove_at(y)
+ _on_rect_change()
+ update()
+
+func update(fllbck : bool = true) -> void:
+ if !_enable_update:
+ return
+ if _updating:
+ return
+ _updating = true
+ var tab : TabBar = _reference
+ if !is_instance_valid(tab):
+ set_deferred(&"_updating", false)
+ return
+
+ for x : int in range(buttons.size() -1, -1, -1):
+ var _container : Variant = buttons[x]
+ if is_instance_valid(_container):
+ continue
+ buttons.remove_at(x)
+
+ while buttons.size() < tab.tab_count:
+ var btn : Control = TAB.instantiate()
+ var control : Control = btn.get_button()
+ var cls : Button = btn.get_button_close()
+
+ if style:
+ btn.set(&"theme_override_styles/panel", style)
+ if !control.gui_input.is_connected(_on_gui_pressed):
+ control.gui_input.connect(_on_gui_pressed.bind(control))
+ if !control.pressed.is_connected(_on_pressed):
+ control.pressed.connect(_on_pressed.bind(control))
+ if !cls.pressed.is_connected(_on_close):
+ cls.pressed.connect(_on_close.bind(control))
+ if !btn.on_pin.is_connected(_on_pin):
+ btn.on_pin.connect(_on_pin)
+ buttons.append(btn)
+
+ while buttons.size() > tab.tab_count:
+ var btn : Variant = buttons.pop_back()
+ if is_instance_valid(btn):
+ if btn is Node:
+ btn.queue_free()
+ else:
+ btn.free()
+
+ if pins.size() > 0:
+ var indx : int = 0
+ var control : Node = tab.get_parent_control()
+ if control:
+ for x : int in range(control.get_child_count()):
+ if x > -1 and tab.tab_count > x:
+ if pins.has(tab.get_tab_tooltip(x)):
+ if x != indx:
+ if x < control.get_child_count():
+ control.move_child(control.get_child(x), indx)
+ indx += 1
+
+ var alpha_pin : Color = Color.WHITE
+ var errors : bool = false
+ alpha_pin.a = 0.25
+
+ for x : int in range(tab.tab_count):
+ var _container : Control = buttons[x]
+ var btn : Button = _container.get_button()
+ var pin : Button = _container.get_button_pin()
+ btn.tooltip_text = tab.get_tab_tooltip(x)
+ _container.set_text(tab.get_tab_title(x))
+ btn.icon = tab.get_tab_icon(x)
+
+ #if fllbck and (btn.tooltip_text.is_empty() or btn.text.begins_with("@VSplitContainer") or btn.text.begins_with("@VBoxContainer")):
+ #if btn.text.begins_with("@VSplitContainer") or btn.text.begins_with("@VBoxContainer"):
+ #btn.text = "File"
+ #errors = true
+
+ if pin:
+ if pins.has(btn.tooltip_text):
+ _container.is_pinned = true
+ pin.set(&"theme_override_colors/icon_normal_color",_select_color)
+ elif _container.is_pinned:
+ _container.is_pinned = false
+ pin.set(&"theme_override_colors/icon_normal_color", alpha_pin)
+
+ btn.set(&"theme_override_colors/icon_normal_color", Color.GRAY)
+ _container.color_rect.visible = false
+ _container.modulate.a = 0.85
+
+
+ if tab.current_tab > -1 and tab.current_tab < buttons.size():
+ var _container : Control = buttons[tab.current_tab]
+ var btn : Button = _container.get_button()
+
+ btn.set(&"theme_override_colors/icon_normal_color", _select_color)
+ _container.modulate.a = 1.0
+
+ var c : ColorRect = _container.color_rect
+ c.visible = true
+ c.color = _select_color
+
+ _on_rect_change()
+
+ if fllbck and errors:
+ Engine.get_main_loop().create_timer(3.0).timeout.connect(update.bind(false))
+
+ set_deferred(&"_updating", false)
+
+func _on_close(btn : Button) -> void:
+ if is_instance_valid(_reference):
+ for x : int in range(0, _reference.tab_count, 1):
+ if _reference.get_tab_tooltip(x) == btn.tooltip_text:
+ _reference.tab_close_pressed.emit(x)
+ break
+
+func _ready() -> void:
+ size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ size_flags_vertical = Control.SIZE_FILL
+
+ item_rect_changed.connect(_on_rect_change)
+
+
+ var bd : Control = EditorInterface.get_base_control()
+ if bd:
+ style = bd.get_theme_stylebox("panel", "")
+ if is_instance_valid(style):
+ style = style.duplicate()
+ if style is StyleBoxFlat:
+ style.border_width_top = 0.0
+ style.border_width_left = 0.0
+ style.border_width_right = 0.0
+ style.border_width_bottom = 0.0
+ style.expand_margin_left = 2.0
+ style.content_margin_bottom = 0.0
+ style.content_margin_top = 0.0
+ style.content_margin_left = 0.0
+ style.content_margin_right = 0.0
+
+
+func _on_rect_change() -> void:
+ if !_enable_update:
+ return
+ _dlt = 0.0
+ _try = 0
+ set_physics_process(true)
+
+func get_reference() -> TabBar:
+ return _reference
+
+func _physics_process(delta: float) -> void:
+ _dlt += delta
+ if _dlt < 0.005:
+ return
+ _dlt = 0.0
+ _try += 1
+ if _try % 2 == 0:
+ return
+ set_physics_process(_try < 30)
+ var rsize : Vector2 = get_parent().get_parent().size
+ if rsize.x > 10.0:
+ for x : Node in container.get_children():
+ container.remove_child(x)
+
+ for x : Control in buttons:
+ var p : Node = x.get_parent()
+ if p:
+ p.remove_child(x)
+
+ var current : HBoxContainer = null
+
+ var index : int = 0
+
+ var min_size : float = 0.0
+ var btn_size : float = 0.0
+ for x : Control in buttons:
+ var bsize : float = x.get_rect().size.x
+ if current == null or (bsize > 0.0 and rsize.x < current.get_minimum_size().x + bsize + 12):
+ if hbox.size() > index:
+ current = hbox[index]
+ else:
+ current = HBoxContainer.new()
+ current.set(&"theme_override_constants/separation", 4)
+ hbox.append(current)
+ index += 1
+ container.add_child(current)
+ current.add_child(x)
+ btn_size = maxf(btn_size, x.size.y)
+ if current:
+ var indx : int = current.get_index() + 1
+ min_size = indx * (btn_size) #+ 12.5
+
+ if custom_minimum_size.y != min_size:
+ _try = 0
+ set_physics_process(true)
+ custom_minimum_size.y = min_size
+
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.gd.uid
new file mode 100644
index 0000000..151c97a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.gd.uid
@@ -0,0 +1 @@
+uid://cnubduf2plac4
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.tscn b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.tscn
new file mode 100644
index 0000000..74d4420
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.tscn
@@ -0,0 +1,16 @@
+[gd_scene load_steps=2 format=3 uid="uid://c86tj28gq8d6t"]
+
+[ext_resource type="Script" uid="uid://cnubduf2plac4" path="res://addons/script_splitter/core/ui/multi_split_container/taby/container.gd" id="1_fdccq"]
+
+[node name="Container" type="PanelContainer" node_paths=PackedStringArray("container")]
+z_index = 10
+anchors_preset = 10
+anchor_right = 1.0
+grow_horizontal = 2
+size_flags_horizontal = 3
+script = ExtResource("1_fdccq")
+container = NodePath("Container")
+
+[node name="Container" type="VBoxContainer" parent="."]
+layout_mode = 2
+theme_override_constants/separation = 1
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container_button.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container_button.gd
new file mode 100644
index 0000000..5ff7325
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container_button.gd
@@ -0,0 +1,69 @@
+@tool
+extends Control
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+signal on_pin(button : Object)
+
+@export var color_rect : ColorRect
+@export var button_main : Button
+@export var button_close : Button
+@export var button_pin : Button
+@export var changes : Label
+
+var is_pinned : bool = false
+
+func _ready() -> void:
+ mouse_entered.connect(_on_enter)
+ mouse_exited.connect(_on_exit)
+
+ var c : Color = Color.WHITE
+ c.a = 0.25
+ button_close.set(&"theme_override_colors/icon_normal_color", c)
+ if !is_pinned:
+ button_pin.set(&"theme_override_colors/icon_normal_color", c)
+ _on_exit()
+
+func _on_enter() -> void:
+ add_to_group(&"__SPLITER_BUTTON_TAB__")
+
+func _on_exit() -> void:
+ remove_from_group(&"__SPLITER_BUTTON_TAB__")
+
+
+func get_reference() -> TabBar:
+ return get_parent().get_parent().get_parent().get_reference()
+
+func get_button_pin() -> Button:
+ return button_pin
+
+func _on_pin_pressed() -> void:
+ on_pin.emit(self)
+
+func set_close_visible(e : bool) -> void:
+ button_close.visible = e
+
+func set_src(src : String) -> void:
+ button_main.tooltip_text = src
+
+func get_src() -> String:
+ return button_main.tooltip_text
+
+func set_text(txt : String) -> void:
+ if txt.ends_with("(*)"):
+ button_main.text = txt.trim_suffix("(*)")
+ changes.modulate.a = 1.0
+ return
+ button_main.text = txt
+ changes.modulate.a = 0.0
+
+func get_button() -> Button:
+ return button_main
+
+func get_button_close() -> Button:
+ return button_close
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container_button.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container_button.gd.uid
new file mode 100644
index 0000000..7b8e1de
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container_button.gd.uid
@@ -0,0 +1 @@
+uid://c3hg84uuly33f
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.gd
new file mode 100644
index 0000000..2949c3f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.gd
@@ -0,0 +1,39 @@
+@tool
+extends VSeparator
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+var _delta : float = 0.0
+var _ref : Control = null
+
+func _ready() -> void:
+ visible = false
+ z_index = RenderingServer.CANVAS_ITEM_Z_MAX - 1
+ z_as_relative = false
+ set_process(_ref != null)
+
+func update(ref : Control) -> void:
+ _ref = ref
+ _delta = 0.0
+ visible = _ref != null
+ set_process(visible)
+
+func delete() -> void:
+ _delta = 10.0
+ _ref = null
+ queue_free()
+
+func _process(delta: float) -> void:
+ _delta += delta
+ if _delta < 0.5:
+ return
+ if is_instance_valid(_ref) and is_inside_tree():
+ if _ref.get_global_rect().has_point(get_global_mouse_position()):
+ return
+ if !is_queued_for_deletion():
+ queue_free()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.gd.uid
new file mode 100644
index 0000000..b0e41f3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.gd.uid
@@ -0,0 +1 @@
+uid://d05p8nj7f18s0
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.tscn b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.tscn
new file mode 100644
index 0000000..6b58fc3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.tscn
@@ -0,0 +1,19 @@
+[gd_scene load_steps=3 format=3 uid="uid://dwe7jhlq2p40h"]
+
+[ext_resource type="Script" uid="uid://d05p8nj7f18s0" path="res://addons/script_splitter/core/ui/multi_split_container/taby/separator.gd" id="1_byjdv"]
+
+[sub_resource type="StyleBoxLine" id="StyleBoxLine_ksadu"]
+color = Color(0.4, 0.4, 1, 1)
+grow_begin = 4.0
+grow_end = 4.0
+thickness = 38
+
+[node name="HSeparator" type="VSeparator"]
+visible = false
+z_index = 4095
+z_as_relative = false
+offset_right = 4.0
+offset_bottom = 4.0
+mouse_filter = 2
+theme_override_styles/separator = SubResource("StyleBoxLine_ksadu")
+script = ExtResource("1_byjdv")
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.gd
new file mode 100644
index 0000000..941291f
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.gd
@@ -0,0 +1,222 @@
+@tool
+extends Button
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const SEPARATOR = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/separator.tscn")
+static var line : VSeparator = null
+
+
+var _delta : float = 0.0
+
+var is_drag : bool = false:
+ set(e):
+ is_drag = e
+ if is_drag:
+ on_drag()
+ Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
+ else:
+ out_drag()
+ if Input.mouse_mode != Input.MOUSE_MODE_VISIBLE:
+ Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
+
+var _fms : float = 0.0
+
+func _ready() -> void:
+ auto_translate_mode = Node.AUTO_TRANSLATE_MODE_DISABLED
+ set_process(false)
+ add_to_group(&"SP_TAB_BUTTON")
+ setup()
+
+func on_drag() -> void:
+ var tab : TabBar = owner.get_reference()
+ if tab:
+ for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
+ if x.has_method(&"dragged"):
+ x.call(&"dragged", tab, true)
+
+func out_drag() -> void:
+ var tab : TabBar = owner.get_reference()
+ if tab:
+ for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
+ if x.has_method(&"dragged"):
+ x.call(&"dragged", tab, false)
+
+func _get_drag_data(__ : Vector2) -> Variant:
+ var c : Control = duplicate(0)
+ c.z_index = RenderingServer.CANVAS_ITEM_Z_MAX - 2
+ set_drag_preview(c)
+ pressed.emit()
+ return self
+
+func _drop_data(_at_position: Vector2, data: Variant) -> void:
+ if is_instance_valid(line):
+ line.delete()
+ if data is Node:
+ if data == self:
+ return
+ elif data.is_in_group(&"SP_TAB_BUTTON"):
+ line.update(self)
+ var node : Node = owner
+ if node:
+ var idx : int = node.get_index()
+ if idx >= 0:
+ var _node : Node = data.owner
+ var lft : bool = false
+ if get_global_mouse_position().x <= get_global_rect().get_center().x:
+ lft = true
+
+ var root : Node = _node
+ for __ : int in range(3):
+ root = root.get_parent()
+ if !is_instance_valid(root):
+ out_drag()
+ return
+
+ for x : Node in get_tree().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ if x.has_method(&"get_builder"):
+ var o : Object = x.call(&"get_builder")
+ if o.has_method(&"swap_by_src"):
+ o.call(&"swap_by_src", data.tooltip_text, tooltip_text, lft)
+ break
+ if root:
+ if root.has_method(&"update"):
+ root.call(&"update")
+
+ out_drag()
+
+func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
+ if data is Node:
+ if data == self:
+ return false
+ elif data.is_in_group(&"SP_TAB_BUTTON"):
+ _delta = 0.0
+ if !is_instance_valid(line):
+ line = SEPARATOR.instantiate()
+ var root : Node = Engine.get_main_loop().root
+ if root:
+ root.add_child(line)
+ if line:
+ var rct : Rect2 = owner.get_global_rect()
+ line.update(self)
+ if at_position.x <= size.x * 0.5:
+ line.global_position = rct.position
+ else:
+ line.global_position = Vector2(rct.end.x, rct.position.y)
+
+ var style : StyleBoxLine = line.get(&"theme_override_styles/separator")
+ style.set(&"thickness",size.y)
+ style.set(&"color",owner.color_rect.color)
+ return true
+ return false
+
+func reset() -> void:
+ if is_drag:
+ set_process(false)
+ is_drag = false
+ if is_inside_tree():
+ var parent : Node = self
+
+ for __ : int in range(10):
+ parent = parent.get_parent()
+ if parent.has_signal(&"out_dragging"):
+ break
+ if !is_instance_valid(parent):
+ return
+ if parent.has_signal(&"out_dragging"):
+ for x : Node in parent.get_children():
+ if x is TabContainer:
+ parent.emit_signal(&"out_dragging",x.get_tab_bar())
+ return
+
+
+
+func _init() -> void:
+ if is_node_ready():
+ _ready()
+
+func _enter_tree() -> void:
+ if !is_in_group(&"__SPLITER_TAB__"):
+ add_to_group(&"__SPLITER_TAB__")
+ if is_node_ready():
+ return
+ owner.modulate.a = 0.0
+ get_tree().create_tween().tween_property(owner, "modulate:a", 1.0, 0.5)
+
+func _exit_tree() -> void:
+ if is_in_group(&"__SPLITER_TAB__"):
+ remove_from_group(&"__SPLITER_TAB__")
+
+func _process(delta: float) -> void:
+ _fms += delta
+ if _fms > 0.24:
+ if is_drag:
+ if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
+ set_process(false)
+ is_drag = false
+ var parent : Node = self
+
+ for __ : int in range(10):
+ parent = parent.get_parent()
+ if parent.has_signal(&"out_dragging"):
+ break
+ if !is_instance_valid(parent):
+ return
+ if parent.has_signal(&"out_dragging"):
+ for x : Node in parent.get_children():
+ if x is TabContainer:
+ parent.emit_signal(&"out_dragging",x.get_tab_bar())
+ return
+ else:
+ if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
+ set_process(false)
+ return
+ is_drag = true
+ var parent : Node = self
+ for __ : int in range(10):
+ parent = parent.get_parent()
+ if parent.has_signal(&"on_dragging"):
+ break
+ if !is_instance_valid(parent):
+ return
+ if parent.has_signal(&"on_dragging"):
+ for x : Node in parent.get_children():
+ if x is TabContainer:
+ parent.emit_signal(&"on_dragging",x.get_tab_bar())
+ return
+
+func setup() -> void:
+ if !gui_input.is_connected(_on_input):
+ gui_input.connect(_on_input)
+ if !is_in_group(&"__SPLITER_TAB__"):
+ add_to_group(&"__SPLITER_TAB__")
+
+func _on_input(e : InputEvent) -> void:
+ if e is InputEventMouseButton:
+ if e.button_index == MOUSE_BUTTON_LEFT:
+ is_drag = false
+ if e.pressed:
+ _fms = 0.0
+ set_process(true)
+ else:
+ set_process(false)
+ if _fms >= 0.24:
+ var parent : Node = self
+ for __ : int in range(10):
+ parent = parent.get_parent()
+ if parent.has_signal(&"out_dragging"):
+ break
+ if !is_instance_valid(parent):
+ return
+ if parent.has_signal(&"out_dragging"):
+ for x : Node in parent.get_children():
+ if x is TabContainer:
+ parent.emit_signal(&"out_dragging",x.get_tab_bar())
+ return
+ #elif e.button_index == MOUSE_BUTTON_RIGHT:
+ #pressed.emit()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.gd.uid
new file mode 100644
index 0000000..b7e4df5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.gd.uid
@@ -0,0 +1 @@
+uid://cf447gbwtnwsl
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.tscn b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.tscn
new file mode 100644
index 0000000..ac36500
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/tab.tscn
@@ -0,0 +1,79 @@
+[gd_scene load_steps=7 format=3 uid="uid://bei2ybryufpnd"]
+
+[ext_resource type="Script" uid="uid://c3hg84uuly33f" path="res://addons/script_splitter/core/ui/multi_split_container/taby/container_button.gd" id="1_c6ml5"]
+[ext_resource type="Script" uid="uid://cf447gbwtnwsl" path="res://addons/script_splitter/core/ui/multi_split_container/taby/tab.gd" id="2_gel5x"]
+[ext_resource type="Script" uid="uid://bkj4lec06udg5" path="res://addons/script_splitter/core/ui/multi_split_container/taby/changes.gd" id="3_f5jk5"]
+[ext_resource type="Texture2D" uid="uid://7rel5pr2g7d2" path="res://addons/script_splitter/assets/pin.svg" id="3_y1lda"]
+[ext_resource type="Texture2D" uid="uid://4juherhkw8hp" path="res://addons/script_splitter/assets/Close.svg" id="4_b84sh"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hvct0"]
+
+[node name="PanelContainer" type="PanelContainer" node_paths=PackedStringArray("color_rect", "button_main", "button_close", "button_pin", "changes")]
+offset_right = 49.0
+offset_bottom = 33.0
+theme_override_styles/panel = SubResource("StyleBoxFlat_hvct0")
+script = ExtResource("1_c6ml5")
+color_rect = NodePath("VBoxContainer/Selected")
+button_main = NodePath("VBoxContainer/ButtonContainer/PanelContainer")
+button_close = NodePath("VBoxContainer/ButtonContainer/Close")
+button_pin = NodePath("VBoxContainer/ButtonContainer/Pin")
+changes = NodePath("VBoxContainer/ButtonContainer/Changes")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+theme_override_constants/separation = 0
+
+[node name="Selected" type="ColorRect" parent="VBoxContainer"]
+custom_minimum_size = Vector2(0, 2)
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 0
+mouse_filter = 1
+
+[node name="ButtonContainer" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 0
+
+[node name="PanelContainer" type="Button" parent="VBoxContainer/ButtonContainer"]
+auto_translate_mode = 2
+layout_mode = 2
+focus_mode = 0
+mouse_filter = 1
+theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
+theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
+theme_override_colors/icon_normal_color = Color(1, 1, 1, 1)
+button_mask = 3
+shortcut_feedback = false
+shortcut_in_tooltip = false
+flat = true
+script = ExtResource("2_gel5x")
+
+[node name="Changes" type="Label" parent="VBoxContainer/ButtonContainer"]
+layout_mode = 2
+mouse_filter = 1
+text = "*"
+script = ExtResource("3_f5jk5")
+
+[node name="Pin" type="Button" parent="VBoxContainer/ButtonContainer"]
+layout_mode = 2
+focus_mode = 0
+mouse_filter = 1
+theme_override_colors/icon_normal_color = Color(1, 1, 1, 0.25)
+shortcut_feedback = false
+shortcut_in_tooltip = false
+icon = ExtResource("3_y1lda")
+flat = true
+icon_alignment = 1
+
+[node name="Close" type="Button" parent="VBoxContainer/ButtonContainer"]
+layout_mode = 2
+focus_mode = 0
+mouse_filter = 1
+theme_override_colors/font_color = Color(1, 1, 1, 0.25)
+theme_override_colors/icon_normal_color = Color(1, 1, 1, 0.25)
+shortcut_feedback = false
+shortcut_in_tooltip = false
+icon = ExtResource("4_b84sh")
+flat = true
+
+[connection signal="pressed" from="VBoxContainer/ButtonContainer/Pin" to="." method="_on_pin_pressed"]
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/Pin.tscn b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/Pin.tscn
new file mode 100644
index 0000000..cbd9174
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/Pin.tscn
@@ -0,0 +1,33 @@
+[gd_scene load_steps=4 format=3 uid="uid://cnbyjwmktxipx"]
+
+[ext_resource type="Script" uid="uid://w8t1dg3mooti" path="res://addons/script_spliter/core/ui/pin/control.gd" id="1_2gb5h"]
+[ext_resource type="Script" uid="uid://cl3i4qjav3ikw" path="res://addons/script_spliter/core/ui/pin/root.gd" id="1_e0xng"]
+[ext_resource type="Script" uid="uid://c34ix3uq8fdae" path="res://addons/script_spliter/core/ui/pin/pin.gd" id="1_ulj15"]
+
+[node name="Control" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_e0xng")
+
+[node name="Control" type="ScrollContainer" parent="."]
+custom_minimum_size = Vector2(0, 16)
+layout_mode = 1
+anchors_preset = 10
+anchor_right = 1.0
+offset_right = -1152.0
+offset_bottom = 36.0
+grow_horizontal = 2
+rotation = 1.5707964
+pivot_offset = Vector2(12.06, 12.42)
+size_flags_horizontal = 0
+size_flags_vertical = 0
+vertical_scroll_mode = 0
+script = ExtResource("1_2gb5h")
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Control"]
+layout_mode = 2
+script = ExtResource("1_ulj15")
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/control.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/control.gd
new file mode 100644
index 0000000..2e9f4ff
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/control.gd
@@ -0,0 +1,14 @@
+@tool
+extends Control
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func _draw() -> void:
+ pivot_offset.x = size.y * 0.335
+ pivot_offset.y = size.y * 0.345
+ size.x = get_child(0).size.x
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/control.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/control.gd.uid
new file mode 100644
index 0000000..ce599d2
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/control.gd.uid
@@ -0,0 +1 @@
+uid://w8t1dg3mooti
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/pin.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/pin.gd
new file mode 100644
index 0000000..746e507
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/pin.gd
@@ -0,0 +1,15 @@
+@tool
+extends HBoxContainer
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+func _enter_tree() -> void:
+ add_to_group(&"__SP_PIN_ROOT__")
+
+func _exit_tree() -> void:
+ remove_from_group(&"__SP_PIN_ROOT__")
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/pin.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/pin.gd.uid
new file mode 100644
index 0000000..e8cf252
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/pin.gd.uid
@@ -0,0 +1 @@
+uid://c34ix3uq8fdae
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/root.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/root.gd
new file mode 100644
index 0000000..9c21e06
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/root.gd
@@ -0,0 +1,10 @@
+extends Control
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+func get_root() -> Node:
+ return get_node("./../../")
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/root.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/root.gd.uid
new file mode 100644
index 0000000..8a4982a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/pin/root.gd.uid
@@ -0,0 +1 @@
+uid://cl3i4qjav3ikw
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd
new file mode 100644
index 0000000..23f8130
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd
@@ -0,0 +1,41 @@
+@tool
+extends CodeEdit
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4f
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+const UPDATE_TIME : float = 0.25
+
+var _dlt : float = 0.0
+var _text : String = ""
+var _dlt_update : float = UPDATE_TIME
+
+func set_text_reference(txt : String) -> void:
+ if _text == txt:
+ return
+ _text = txt
+ _dlt = 0.0
+ _dlt_update = UPDATE_TIME + (txt.length() * 0.00001)
+
+ set_process(true)
+
+func _init() -> void:
+ if is_node_ready():
+ _ready()
+
+func _ready() -> void:
+ set_process(false)
+
+func _process(delta: float) -> void:
+ _dlt += delta
+ if _dlt > _dlt_update:
+ set_process(false)
+ var sv : float = scroll_vertical
+ var sh : int = scroll_horizontal
+ text = _text
+ scroll_vertical = sv
+ scroll_horizontal = sh
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd.uid
new file mode 100644
index 0000000..3e84528
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor/ssp_editor.gd.uid
@@ -0,0 +1 @@
+uid://bec3qea1dh8xe
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd
new file mode 100644
index 0000000..24c8919
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd
@@ -0,0 +1,94 @@
+@tool
+extends TabContainer
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const Dottab = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd")
+const CLOSE = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/Close.svg")
+
+const GLOBALS : PackedStringArray = ["_GlobalScope", "_GDScript"]
+
+#
+signal focus(o : TabContainer, index : int)
+signal remove(o : TabContainer, index : int)
+
+var _new_tab_settings : bool = false
+var _tab_queue : int = -1
+
+func _enter_tree() -> void:
+ add_to_group(&"__SC_SPLITTER__")
+
+func _exit_tree() -> void:
+ if is_in_group(&"__SC_SPLITTER__"):
+ remove_from_group(&"__SC_SPLITTER__")
+
+func _ready() -> void:
+ size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+
+ auto_translate_mode = Node.AUTO_TRANSLATE_MODE_DISABLED
+
+ var tb : TabBar = get_tab_bar()
+ if tb:
+ tb.auto_translate_mode = auto_translate_mode
+
+
+ drag_to_rearrange_enabled = true
+
+ #CONNECT
+ var tab : TabBar = get_tab_bar()
+ tab.set_script(Dottab)
+ tab.tab_selected.connect(_on_selected)
+
+ tab.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_ACTIVE_ONLY
+ tab.tab_close_pressed.connect(_on_remove)
+ tab.select_with_rmb = true
+ tab.on_start_drag.connect(on_drag)
+ tab.on_stop_drag.connect(out_drag)
+
+func _set_tab() -> void:
+ if current_tab != _tab_queue and _tab_queue > -1 and _tab_queue < get_tab_count():
+ current_tab = _tab_queue
+ _new_tab_settings = false
+
+func set_tab(index : int) -> void:
+ if index > -1 and index < get_tab_count():
+ _tab_queue = index
+
+ if _new_tab_settings:
+ return
+
+ _new_tab_settings = true
+ _set_tab.call_deferred()
+
+func on_drag(tab : TabBar) -> void:
+ for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
+ if x.has_method(&"dragged"):
+ x.call(&"dragged", tab, true)
+
+func out_drag(tab : TabBar) -> void:
+ for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
+ if x.has_method(&"dragged"):
+ x.call(&"dragged", tab, false)
+
+func _on_remove(index : int) -> void:
+ remove.emit(self, index)
+
+func _on_selected(value : int) -> void:
+ focus.emit(self, value)
+
+func get_root() -> Node:
+ return self
+
+func set_item_tooltip(idx : int, txt : String) -> void:
+ if idx > -1 and get_tab_count() > idx:
+ set_tab_tooltip(idx, txt)
+
+func set_item_text(idx : int, txt : String) -> void:
+ if idx > -1 and get_tab_count() > idx:
+ set_tab_title(idx, txt)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd.uid
new file mode 100644
index 0000000..047e001
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd.uid
@@ -0,0 +1 @@
+uid://b45owls32pkxk
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd
new file mode 100644
index 0000000..8fad1e4
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd
@@ -0,0 +1,89 @@
+@tool
+extends ScrollContainer
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const PIN = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/pin.svg")
+const FILL_EXPAND = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/fill_expand.svg")
+const SPLIT_CPLUS_TOOL = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cplus_tool.svg")
+const SPLIT_MINUS_TOOL = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_minus_tool.svg")
+const SPLIT_PLUS_TOOL = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_plus_tool.svg")
+const SPLIT_RMINUS_TOOL = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rminus_tool.svg")
+const SPLIT_RPLUS_TOOL = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_rplus_tool.svg")
+const SPLIT_CMINUS_TOOL = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/split_cminus_tool.svg")
+
+const PAD : float = 12.0
+
+var _root : VBoxContainer = null
+var _min_size : float = 0.0
+
+@warning_ignore("unused_private_class_variable")
+var _pin_root : Control = null
+
+func _ready() -> void:
+ _root = VBoxContainer.new()
+ _root.alignment = BoxContainer.ALIGNMENT_BEGIN
+ _root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ _root.size_flags_vertical = Control.SIZE_EXPAND_FILL
+ add_child(_root)
+ _setup()
+
+ custom_minimum_size.x = _min_size + PAD
+ horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
+ vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER
+
+func get_root() -> Node:
+ return _root
+
+func _enter_tree() -> void:
+ add_to_group(&"__script_splitter__IO__")
+
+func _exit_tree() -> void:
+ remove_from_group(&"__script_splitter__IO__")
+
+# Traduction?
+func _tr(st : String) -> String:
+ # ...
+ return st.capitalize()
+
+func _setup() -> void:
+ make_function(&"EXPAND", FILL_EXPAND, _tr("Expand/Unexpand current tab container"))
+ make_function(&"SPLIT_COLUMN", SPLIT_CPLUS_TOOL, _tr("Split to new column"))
+ make_function(&"MERGE_COLUMN", SPLIT_CMINUS_TOOL, _tr("Merge current column"))
+ make_function(&"SPLIT_ROW", SPLIT_RPLUS_TOOL, _tr("Split to new row"))
+ make_function(&"MERGE_ROW", SPLIT_RMINUS_TOOL, _tr("Merge current row"))
+ make_function(&"SPLIT_SUB", SPLIT_PLUS_TOOL, _tr("Sub Split current editor"))
+ make_function(&"MERGE_SPLIT_SUB", SPLIT_MINUS_TOOL, _tr("Merge sub split of current editor"))
+
+func enable(id : StringName, e : bool) -> void:
+ for x : Node in _root.get_children():
+ if x.name == id:
+ x.set(&"disabled", !e)
+
+func get_button(id : String) -> Button:
+ if _root.has_node(id):
+ var node : Node = _root.get_node(id)
+ if node is Button:
+ return node
+ return null
+
+func make_function(id : StringName, icon : Texture2D = null, txt : String = "") -> void:
+ var btn : Button = Button.new()
+ btn.name = id
+
+ btn.pressed.connect(_call.bind(id))
+ btn.icon = icon
+ btn.alignment = HORIZONTAL_ALIGNMENT_CENTER
+ btn.tooltip_text = txt
+ btn.flat = is_instance_valid(icon)
+ _min_size = maxf(icon.get_size().x, _min_size)
+ _root.add_child(btn)
+
+func _call(id : StringName) -> void:
+ for x : Node in get_tree().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
+ if x.has_method(&"_io_call"):
+ x.call(&"_io_call", id)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd.uid
new file mode 100644
index 0000000..3d47ef5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/io/io_bar.gd.uid
@@ -0,0 +1 @@
+uid://b817wwia7hrqd
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd
new file mode 100644
index 0000000..e5b4222
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd
@@ -0,0 +1,292 @@
+@tool
+extends MarginContainer
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const SplitterRoot = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd")
+const HandlerContainer = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/base/container.gd")
+const BaseContainerItem = preload("splitter_item.gd")
+
+const SplitterEditorContainer = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd")
+
+const Overlay = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd")
+const CODE_NAME_TWISTER = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/github_CodeNameTwister.svg")
+
+var _handler_container : HandlerContainer = null
+var _base_container : TabContainer = null
+
+var _root : Container = null
+var _root_container : SplitterRoot = null
+
+var _last_editor_container : SplitterEditorContainer.Editor = null
+
+var _overlay : Overlay = null
+
+var swap_by_button : bool = true
+
+func get_root() -> SplitterRoot:
+ return _root_container
+
+func get_base_editors() -> Array[Node]:
+ return _base_container.get_children()
+
+func initialize(container : TabContainer, handler_container : HandlerContainer) -> void:
+ _setup()
+
+ _handler_container = handler_container
+ _base_container = container
+
+ _root = self
+
+ var credits : TextureRect = TextureRect.new()
+ add_child(credits)
+
+ credits.texture = CODE_NAME_TWISTER
+ credits.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
+ credits.size_flags_vertical = Control.SIZE_SHRINK_CENTER
+ credits.expand_mode = TextureRect.EXPAND_KEEP_SIZE
+ credits.stretch_mode = TextureRect.STRETCH_KEEP_CENTERED
+ credits.modulate.a = 0.25
+
+ var root : Node = _base_container.get_parent()
+ root.add_child(_root)
+ root.move_child(_root, mini(_base_container.get_index(), 0))
+
+ #_root.add_child(_root_container)
+
+ _root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ _root.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ var vspl : HBoxContainer = HBoxContainer.new()
+
+ _root.add_child(vspl)
+ vspl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ vspl.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ var base : SplitterRoot = create_base_container(vspl, false)
+ base.max_columns = 1
+ _root_container = base
+
+
+ vspl.add_child(_handler_container.get_io_bar())
+ initialize_editor_contianer()
+
+ _overlay = Overlay.new()
+ add_child(_overlay)
+
+func _on_change() -> void:
+ var dt : Array = ["plugin/script_splitter/editor/behaviour/swap_by_double_click_separator_button"]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ break
+
+func _setup() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["swap_by_button", "plugin/script_splitter/editor/behaviour/swap_by_double_click_separator_button"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+func initialize_editor_contianer() -> void:
+ if _root_container.get_child_count() > 0:
+ for x : Node in _root_container.get_children():
+ x.queue_free()
+ _last_editor_container = create_new_editor_container(_root_container, true)
+
+func swap(value : Variant) -> void:
+ if !swap_by_button:
+ return
+
+ if !is_instance_valid(value):
+ return
+
+ elif !is_instance_valid(_root_container) or _root_container.get_child_count() == 0:
+ return
+
+ elif !value is SplitterRoot.LineSep:
+ return
+
+ var caller : SplitterRoot.LineSep = value
+
+ var _main : SplitterRoot = caller.get_parent()
+
+ if !is_instance_valid(_main):
+ return
+
+ var separators : Array = _main.get_separators()
+ if separators.size() == 0:
+ return
+
+ var index : int = 0
+ var linesep : Object = null
+ for x : Object in separators:
+ if x == caller:
+ linesep =x
+ break
+ index += 1
+
+ if linesep:
+ if linesep.is_vertical:
+ var atotal : int = 1
+ var btotal : int = 1
+ var nodes : Array[Node] = []
+
+ for x : int in range(index + 1, separators.size(), 1):
+ var clinesep : Object = separators[x]
+ if clinesep.is_vertical:
+ break
+ atotal += 1
+ for x : int in range(index - 1, -1, -1):
+ var clinesep : Object = separators[x]
+ if clinesep.is_vertical:
+ break
+ btotal += 1
+
+ var cindex : int = index
+ while atotal > 0:
+ cindex += 1
+ atotal -= 1
+ if cindex < _main.get_child_count():
+ nodes.append(_main.get_child(cindex))
+ continue
+ break
+
+ for x : Node in nodes:
+ cindex = btotal
+ while cindex > 0:
+ cindex -= 1
+ var idx : int = x.get_index() - 1
+ if _main.get_child_count() > idx:
+ _main.move_child(x, idx)
+ else:
+ index += 1
+ if _main.get_child_count() > index:
+ var child : Node = _main.get_child(index - 1)
+ _main.move_child(child, index)
+
+func _enter_tree() -> void:
+ add_to_group(&"ScriptSplitter")
+
+func _exit_tree() -> void:
+ remove_from_group(&"ScriptSplitter")
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+
+func dragged(tab : TabBar, is_drag : bool) -> void:
+ if is_drag:
+ _overlay.start(tab)
+ else:
+ if _overlay.stop(tab):
+ var container : Node = _overlay.get_container()
+ var from : Container = tab.get_parent()
+ if is_instance_valid(container) and is_instance_valid(from):
+ if from != container:
+ _handler_container.swap_tab.emit(from, tab.current_tab, container)
+
+func create_new_column() -> SplitterEditorContainer.Editor:
+ var item : BaseContainerItem = get_base_container_item(_last_editor_container)
+ var root : Container = get_base_container(_last_editor_container)
+ var index : int = item.get_index()
+ var custom_position : bool = index >= 0 and index < item.get_parent().get_child_count() - 1
+ _last_editor_container = create_editor_container(create_base_container_item(root))
+ if custom_position:
+ root.move_child(get_base_container_item(_last_editor_container), index + 1)
+ return _last_editor_container
+
+func create_new_row() -> SplitterEditorContainer.Editor:
+ var root : Container = get_base_container(_last_editor_container)
+ var index : int = root.get_index()
+ var custom_position : bool = index >= 0 and index < root.get_parent().get_child_count() - 1
+ _last_editor_container = create_new_editor_container(_root_container)# create_editor_container(create_base_container_item(create_base_container(_root_container)))
+ if custom_position:
+ _root_container.move_child(get_base_container(_last_editor_container).get_parent(), index + 1)
+ return _last_editor_container
+
+
+func set_current_editor(container : Node) -> bool:
+ if container is SplitterEditorContainer.Editor:
+ _last_editor_container = container
+ return true
+ return false
+
+func get_base_container(editor : SplitterEditorContainer.Editor) -> Container:
+ return editor.get_node("./../../../")
+
+func get_base_container_item(editor : SplitterEditorContainer.Editor) -> BaseContainerItem:
+ return editor.get_node("./../../")
+
+func create_base_container(c_root : Node, _add_to_group : bool = true) -> Container:
+ var b_root : Container = SplitterRoot.new()
+ b_root.max_columns = 0
+ c_root.add_child(b_root)
+ b_root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ b_root.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ if _add_to_group:
+ b_root.add_to_group(&"__SP_BR__")
+
+ return b_root
+
+
+func create_base_container_item(c_root : Container) -> BaseContainerItem:
+ var b_item : BaseContainerItem = BaseContainerItem.new()
+ c_root.add_child(b_item)
+
+ b_item.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ b_item.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ return b_item
+
+func create_editor_container(c_root : BaseContainerItem) -> SplitterEditorContainer.Editor:
+ var b_editor : SplitterEditorContainer = SplitterEditorContainer.new()
+
+ c_root.add_child(b_editor)
+ b_editor.get_editor()
+
+ var editor : SplitterEditorContainer.Editor = b_editor.get_editor()
+
+ editor.focus.connect(_handler_container.on_focus)
+ editor.remove.connect(_handler_container.on_remove)
+
+ editor.get_tab_bar().tab_rmb_clicked.connect(_on_rmb_clicked.bind(editor))
+ return editor
+
+func _on_rmb_clicked(index : int, tab : Variant) -> void:
+ if tab is SplitterEditorContainer.Editor:
+ _handler_container.rmb_click.emit(index, tab)
+
+func create_new_editor_container(c_root : Node, _add_to_group : bool = true) -> SplitterEditorContainer.Editor:
+ return create_editor_container(create_base_container_item(create_base_container(c_root, _add_to_group)))
+
+func get_current_editor() -> SplitterEditorContainer.Editor:
+ return _last_editor_container
+
+func reset() -> void:
+ _root.queue_free()
+ _base_container.visible = true
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+func notify_creation() -> void:
+ if _base_container.visible:
+ _base_container.visible = false
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd.uid
new file mode 100644
index 0000000..377bda5
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_container.gd.uid
@@ -0,0 +1 @@
+uid://dyo7c2g4uwn0g
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd
new file mode 100644
index 0000000..b2ef7ba
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd
@@ -0,0 +1,80 @@
+extends VBoxContainer
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const Editor = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/editor_container.gd")
+const CONTAINER = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/taby/container.tscn")
+
+var _editor : Editor = null
+var _tab_old_behaviour : bool = false:
+ set = _on_behaviour_changed
+var tab : Node = null
+
+func _on_behaviour_changed(e) -> void:
+ _tab_old_behaviour = e
+ if is_instance_valid(tab):
+ tab.set_enable(!_tab_old_behaviour)
+ _editor.tabs_visible = _tab_old_behaviour
+
+func _on_change() -> void:
+ var dt : Array = ["plugin/script_splitter/editor/tabs/use_old_behaviour"]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ break
+
+func _setup() -> void:
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["_tab_old_behaviour", "plugin/script_splitter/editor/tabs/use_old_behaviour"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+func _ready() -> void:
+ _editor = Editor.new()
+
+ set(&"theme_override_constants/separation", -12)
+
+ tab = CONTAINER.instantiate()
+ tab.set_ref(_editor.get_tab_bar())
+ tab.set_enable(!_tab_old_behaviour)
+ _editor.tabs_visible = _tab_old_behaviour
+
+ add_child(tab)
+ add_child(_editor)
+
+ size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+func _enter_tree() -> void:
+ add_to_group(&"__SP_EC__")
+ _setup()
+
+func _exit_tree() -> void:
+ remove_from_group(&"__SP_EC__")
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
+func get_editor() -> Editor:
+ return _editor
+
+func update() -> void:
+ if !is_instance_valid(tab):
+ return
+ tab.update()
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd.uid
new file mode 100644
index 0000000..f094d34
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_editor_container.gd.uid
@@ -0,0 +1 @@
+uid://dlqgddtcsov1m
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_item.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_item.gd
new file mode 100644
index 0000000..f5296c7
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_item.gd
@@ -0,0 +1,67 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/split_container_item.gd"
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+var _fms : float = 0.0
+
+func _ready() -> void:
+ super()
+ modulate = Color.DARK_GRAY
+ set_physics_process(modulate != Color.WHITE)
+
+func _physics_process(delta: float) -> void:
+ _fms += delta * 0.24
+ if _fms >= 1.0:
+ _fms = 1.0
+ set_physics_process(false)
+ modulate = lerp(modulate, Color.WHITE, _fms)
+
+func _enter_tree() -> void:
+ super()
+
+ add_to_group(&"__SP_IC__")
+ var parent : Node = get_parent()
+ if parent.has_method(&"expand_splited_container"):
+ _on_child(self)
+
+func _exit_tree() -> void:
+ add_to_group(&"__SP_IC__")
+
+func _on_child(n : Node) -> void:
+ if n is Control:
+ var parent : Node = get_parent()
+ if !n.child_entered_tree.is_connected(_on_child):
+ n.child_entered_tree.connect(_on_child)
+
+ if !n.child_exiting_tree.is_connected(_out_child):
+ n.child_exiting_tree.connect(_out_child)
+
+ if n.focus_mode != Control.FOCUS_NONE:
+ if !n.focus_entered.is_connected(parent.expand_splited_container):
+ n.focus_entered.connect(parent.expand_splited_container.bind(self))
+
+ for x : Node in n.get_children():
+ _on_child(x)
+
+func _out_child(n : Node) -> void:
+ if n is Control:
+ var parent : Node = get_parent()
+ if n.child_entered_tree.is_connected(_on_child):
+ n.child_entered_tree.disconnect(_on_child)
+
+ if n.child_exiting_tree.is_connected(_out_child):
+ n.child_exiting_tree.disconnect(_out_child)
+
+ if n.focus_mode != Control.FOCUS_NONE:
+ if n.focus_entered.is_connected(parent.expand_splited_container):
+ n.focus_entered.disconnect(parent.expand_splited_container)
+ for x : Node in n.get_children():
+ _out_child(x)
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_item.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_item.gd.uid
new file mode 100644
index 0000000..f9aa33b
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_item.gd.uid
@@ -0,0 +1 @@
+uid://b2kiv55ed0aj
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd
new file mode 100644
index 0000000..34471c1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd
@@ -0,0 +1,87 @@
+@tool
+extends ItemList
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+var _ss: Callable
+var _delta : int = 0
+var _list : ItemList = null
+
+func _ready() -> void:
+ set_physics_process(false)
+
+func update() -> void:
+ _delta = 0
+ set_physics_process(true)
+
+func set_list(item : ItemList) -> void:
+ _list = item
+
+func set_reference(scall : Callable) -> void:
+ _ss = scall
+
+func changes(list : ItemList) -> bool:
+ if list.item_count != item_count:
+ return true
+
+ for x : int in list.item_count:
+ if is_selected(x) != is_selected(x) or \
+ get_item_text(x) != list.get_item_text(x) or\
+ get_item_icon(x) != list.get_item_icon(x) or \
+ get_item_icon_modulate(x) != list.get_item_icon_modulate(x) or \
+ get_item_tooltip(x) != list.get_item_tooltip(x):
+ return true
+
+ return false
+
+func _physics_process(__ : float) -> void:
+ _delta += 1
+ if _delta < 10:
+ return
+ set_physics_process(false)
+ if !_ss.is_valid():
+ return
+ if !changes(_list):
+ return
+ _ss.call()
+
+#
+#var dragged_item_index: int = -1
+#
+#func _get_drag_data(at_position: Vector2) -> Variant:
+ #var item_index = get_item_at_position(at_position)
+#
+ #if item_index != -1:
+ #dragged_item_index = item_index
+ #
+ #var drag_preview : Label = Label.new()
+ #drag_preview.text = get_item_text(item_index)
+ #set_drag_preview(drag_preview)
+ #
+ #return item_index
+ #return null
+#
+#func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
+ #if typeof(data) == TYPE_INT:
+ #var drop_index : int = get_item_at_position(at_position)
+ #
+ #return drop_index != -1 and drop_index != data
+ #
+ #return false
+#
+#func _drop_data(at_position: Vector2, data: Variant) -> void:
+ #if !(data is int):
+ #return
+ #var from_index : int = data as int
+ #
+ #var to_index : int = get_item_at_position(at_position)
+ #
+ #if from_index != -1 and to_index != -1:
+ #_list.move_item(from_index, to_index)
+ #
+ #dragged_item_index = -1
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd.uid
new file mode 100644
index 0000000..b4b29c9
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_list.gd.uid
@@ -0,0 +1 @@
+uid://c12gyjf2qmasp
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd
new file mode 100644
index 0000000..a1f6182
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd
@@ -0,0 +1,108 @@
+@tool
+extends "res://addons/_Godot-IDE_/plugins/script_splitter/core/ui/multi_split_container/multi_split_container.gd"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const EXPAND = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/expand.svg")
+
+var __setup : bool = false
+var _delta : float = 0.0
+
+func _init() -> void:
+ super()
+
+ drag_button_icon = EXPAND
+ drag_button_size = 24.0
+ behaviour_expand_on_focus = true
+ behaviour_can_expand_focus_same_container = false
+ behaviour_expand_smoothed = true
+ drag_button_always_visible = false
+ drag_button_modulate = Color.WHITE
+ behaviour_expand_on_double_click = true
+ behaviour_can_move_by_line = true
+
+ _setup()
+
+func _ready() -> void:
+ super()
+ modulate.a = 0.0
+ set_physics_process(true)
+
+func _physics_process(delta : float) -> void:
+ _delta += delta * 2.0
+ if _delta >= 1.0:
+ _delta = 1.0
+ set_physics_process(false)
+ modulate.a = _delta
+
+
+func _on_change() -> void:
+ var dt : Array = ["plugin/script_splitter/editor/behaviour/expand_on_focus"
+ ,"plugin/script_splitter/editor/behaviour/can_expand_on_same_focus"
+ ,"plugin/script_splitter/editor/behaviour/smooth_expand"
+ ,"plugin/script_splitter/editor/behaviour/smooth_expand_time"
+ ,"plugin/script_splitter/line/size"
+ ,"plugin/script_splitter/line/color"
+ ,"plugin/script_splitter/line/draggable"
+ ,"plugin/script_splitter/line/expand_by_double_click"
+ ,"plugin/script_splitter/line/button/size"
+ ,"plugin/script_splitter/line/button/modulate"
+ ,"plugin/script_splitter/line/button/always_visible"
+ ]
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ var changes : PackedStringArray = settings.get_changed_settings()
+
+ for c in changes:
+ if c in dt:
+ _setup()
+ update()
+ break
+
+func _setup() -> void:
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if !settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.connect(_on_change)
+
+ for x : Array in [
+ ["behaviour_expand_on_focus", "plugin/script_splitter/editor/behaviour/expand_on_focus"]
+ ,["behaviour_can_expand_focus_same_container", "plugin/script_splitter/editor/behaviour/can_expand_on_same_focus"]
+ ,["behaviour_expand_smoothed", "plugin/script_splitter/editor/behaviour/smooth_expand"]
+ ,["drag_button_size", "plugin/script_splitter/editor/behaviour/smooth_expand_time"]
+ ,["separator_line_size", "plugin/script_splitter/line/size"]
+ ,["separator_line_color", "plugin/script_splitter/line/color"]
+ ,["behaviour_can_move_by_line", "plugin/script_splitter/line/draggable"]
+ ,["behaviour_expand_on_double_click", "plugin/script_splitter/line/expand_by_double_click"]
+ ,["drag_button_size", "plugin/script_splitter/line/button/size"]
+ ,["drag_button_modulate", "plugin/script_splitter/line/button/modulate"]
+ ,["drag_button_always_visible", "plugin/script_splitter/line/button/always_visible"]
+ ]:
+ if settings.has_setting(x[1]):
+ set(x[0], settings.get_setting(x[1]))
+ else:
+ settings.set_setting(x[1], get(x[0]))
+
+
+func _enter_tree() -> void:
+ add_to_group(&"__ST_CS__")
+ super()
+
+ if __setup:
+ return
+
+ __setup = true
+
+ _setup()
+
+func _exit_tree() -> void:
+ remove_from_group(&"__ST_CS__")
+
+ var settings : EditorSettings = EditorInterface.get_editor_settings()
+ if settings.settings_changed.is_connected(_on_change):
+ settings.settings_changed.disconnect(_on_change)
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd.uid
new file mode 100644
index 0000000..532e88c
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/splitter_root.gd.uid
@@ -0,0 +1 @@
+uid://cl2dnbvuovoro
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd
new file mode 100644
index 0000000..cb473d1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd
@@ -0,0 +1,78 @@
+@tool
+extends TabBar
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+signal on_start_drag(t : TabBar)
+signal on_stop_drag(t : TabBar)
+
+var is_drag : bool = false:
+ set(e):
+ is_drag = e
+ if is_drag:
+ Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
+ else:
+ Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
+
+var _fms : float = 0.0
+
+func reset() -> void:
+ if is_drag:
+ set_process(false)
+ is_drag = false
+ if is_inside_tree():
+ on_stop_drag.emit(null)
+
+func _init() -> void:
+ if is_node_ready():
+ _ready()
+
+func _ready() -> void:
+ set_process(false)
+ setup()
+
+ select_with_rmb = true
+
+func _enter_tree() -> void:
+ if !is_in_group(&"__SPLITER_TAB__"):
+ add_to_group(&"__SPLITER_TAB__")
+
+func _exit_tree() -> void:
+ if is_in_group(&"__SPLITER_TAB__"):
+ remove_from_group(&"__SPLITER_TAB__")
+
+func _process(delta: float) -> void:
+ _fms += delta
+ if _fms > 0.24:
+ if is_drag:
+ if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
+ set_process(false)
+ is_drag = false
+ on_stop_drag.emit(self)
+ else:
+ on_start_drag.emit(self)
+ is_drag = true
+
+func setup() -> void:
+ if !gui_input.is_connected(_on_input):
+ gui_input.connect(_on_input)
+ if !is_in_group(&"__SPLITER_TAB__"):
+ add_to_group(&"__SPLITER_TAB__")
+
+func _on_input(e : InputEvent) -> void:
+ if e is InputEventMouseButton:
+ if e.button_index == MOUSE_BUTTON_LEFT:
+ is_drag = false
+ if e.pressed:
+ _fms = 0.0
+ set_process(true)
+ else:
+ set_process(false)
+ if _fms >= 0.24:
+ on_stop_drag.emit(self)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd.uid
new file mode 100644
index 0000000..e2af954
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/dottab.gd.uid
@@ -0,0 +1 @@
+uid://bbwrffwhmepvw
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd
new file mode 100644
index 0000000..17a2bbf
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd
@@ -0,0 +1,120 @@
+@tool
+extends ColorRect
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+const FILE_IN = preload("res://addons/_Godot-IDE_/plugins/script_splitter/assets/file_in.png")
+
+
+const NORMAL : float = 0.0
+const FILL : float = 0.65
+
+var _dt : float = 0.0
+var _fc : float = 0.0
+var _ec : float = 1.0
+
+var _ref : TabBar = null
+var _container : Control = null
+
+var txt : TextureRect = null
+
+func _init() -> void:
+ visible = false
+ mouse_filter = Control.MOUSE_FILTER_IGNORE
+ z_as_relative = false
+ z_index = RenderingServer.CANVAS_ITEM_Z_MAX - 1
+
+func start(ref : TabBar) -> void:
+ _fc = NORMAL
+ _ec = FILL
+ _dt = 0.0
+ _ref = ref
+ modulate.a = _fc
+
+ if is_instance_valid(ref):
+ _container = ref.get_parent()
+ else:
+ _container = null
+
+ _update()
+ set_process(true)
+
+func stop(tab : TabBar = null) -> bool:
+ set_process(false)
+ visible = false
+ if is_instance_valid(tab) and tab == _ref:
+ var container : Node = _ref.get_parent()
+ if is_instance_valid(_container) and _container == container:
+ return get_global_rect().has_point(get_global_mouse_position())
+ return false
+
+func get_container() -> Node:
+ for x : Node in get_tree().get_nodes_in_group(&"__SC_SPLITTER__"):
+ if x == _container:
+ continue
+ var root : Node = x.get_parent()
+ if root is Control:
+ var rect : Rect2 = root.get_global_rect()
+ if rect.has_point(get_global_mouse_position()):
+ return x
+ return null
+
+func _ready() -> void:
+ color = Color.DARK_GREEN
+
+ set_process(false)
+ visible = false
+
+ txt = TextureRect.new()
+
+ add_child(txt)
+
+ txt.texture = FILE_IN
+ txt.expand_mode = TextureRect.EXPAND_KEEP_SIZE
+ txt.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
+
+func _update() -> void:
+ if is_instance_valid(_container):
+ for x : Node in get_tree().get_nodes_in_group(&"__SC_SPLITTER__"):
+ if x == _container:
+ continue
+ var root : Node = x.get_parent()
+ if root is Control:
+ var rect : Rect2 = root.get_global_rect()
+ if rect.has_point(get_global_mouse_position()):
+ size = root.size
+ global_position = root.global_position
+ txt.global_position = get_global_rect().get_center() - (txt.texture.get_size() * 0.5)
+ if !visible:
+ visible = true
+ return
+
+ _fc = NORMAL
+ _ec = FILL
+ _dt = 0.0
+ modulate.a = _fc
+ visible = false
+
+func _process(delta: float) -> void:
+ _update()
+
+ if !visible:
+ return
+
+ _dt += delta * 2.0
+ if _dt >= 1.0:
+ modulate.a = _ec
+ if _ec == FILL:
+ _ec = NORMAL
+ _fc = FILL
+ else:
+ _ec = FILL
+ _fc = NORMAL
+ _dt = 0.0
+ return
+ modulate.a = lerpf(_fc, _ec, _dt)
+
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd.uid
new file mode 100644
index 0000000..d3fafe3
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/ui/splitter/taby/overlay.gd.uid
@@ -0,0 +1 @@
+uid://lo116b5bry0t
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd b/addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd
new file mode 100644
index 0000000..3d2dfdc
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd
@@ -0,0 +1,22 @@
+@tool
+extends Node
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+signal notification(what : int)
+
+func _notification(what: int) -> void:
+ notification.emit(what)
+
+func panic() -> void:
+ if !tree_exiting.is_connected(_on_exiting):
+ tree_exiting.connect(_on_exiting)
+
+func _on_exiting() -> void:
+ notification.emit(NOTIFICATION_PREDELETE)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd.uid
new file mode 100644
index 0000000..07902e0
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/core/util/control.gd.uid
@@ -0,0 +1 @@
+uid://d3whf030xddd3
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/plugin.cfg b/addons/_Godot-IDE_/plugins/script_splitter/plugin.cfg
new file mode 100644
index 0000000..a5e5ffb
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/plugin.cfg
@@ -0,0 +1,9 @@
+[plugin]
+
+name="Script Splitter"
+description="Tool Addon for godot 4
+Allow split script window."
+author="Twister"
+version="0.5-DEV-2.2"
+github="https://github.com/CodeNameTwister/Script-Splitter"
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/plugin.gd b/addons/_Godot-IDE_/plugins/script_splitter/plugin.gd
new file mode 100644
index 0000000..4b6a839
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/plugin.gd
@@ -0,0 +1,85 @@
+@tool
+extends EditorPlugin
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Script Splitter
+# https://github.com/CodeNameTwister/Script-Splitter
+#
+# Script Splitter addon for godot 4
+# author: "Twister"
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+const InputTool = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/Input.gd")
+const TWISTER_script_splitter = preload("res://addons/_Godot-IDE_/plugins/script_splitter/core/builder.gd")
+var builder : TWISTER_script_splitter = null
+var handler : InputTool = null
+
+var tab_container : Node = null:
+ get:
+ if !is_instance_valid(tab_container):
+ tab_container = IDE.get_script_editor_container()
+ return tab_container
+var item_list : Node = null:
+ get:
+ if !is_instance_valid(item_list):
+ item_list = IDE.get_script_list()
+ return item_list
+
+func find(root : Node, pattern : String, type : String) -> Node:
+ var e : Array[Node] = root.find_children(pattern, type, true, false)
+ if e.size() > 0:
+ return e[0]
+ return null
+
+func _enter_tree() -> void:
+ add_to_group(&"__SCRIPT_SPLITTER__")
+ builder = TWISTER_script_splitter.new()
+ handler = InputTool.new(self, builder)
+
+func script_split() -> void:
+ handler.get_honey_splitter().split()
+
+func script_merge(value : Node = null) -> void:
+ handler.get_honey_splitter().merge(value)
+
+func _ready() -> void:
+ set_process(false)
+ set_process_input(false)
+ for __ : int in range(5):
+ await Engine.get_main_loop().process_frame
+ if is_instance_valid(builder):
+ builder.init_1(self, tab_container, item_list)
+ if is_instance_valid(handler):
+ handler.init_1()
+
+ builder.connect_callbacks(
+ handler.add_column,
+ handler.add_row,
+ handler.remove_column,
+ handler.remove_row,
+ handler.left_tab_close,
+ handler.right_tab_close,
+ handler.others_tab_close
+ )
+
+func _save_external_data() -> void:
+ builder.refresh_warnings()
+
+func _exit_tree() -> void:
+ remove_from_group(&"__SCRIPT_SPLITTER__")
+ for x : Variant in [handler, builder]:
+ if is_instance_valid(x) and x is Object:
+ x.call(&"init_0")
+
+func get_builder() -> Object:
+ return builder
+
+func _process(delta: float) -> void:
+ builder.update(delta)
+
+func _input(event: InputEvent) -> void:
+ if handler.event(event):
+ get_viewport().set_input_as_handled()
+
+func _io_call(id : StringName) -> void:
+ builder.handle(id)
diff --git a/addons/_Godot-IDE_/plugins/script_splitter/plugin.gd.uid b/addons/_Godot-IDE_/plugins/script_splitter/plugin.gd.uid
new file mode 100644
index 0000000..8dc92c7
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/script_splitter/plugin.gd.uid
@@ -0,0 +1 @@
+uid://dxveb5g4kxu45
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd
new file mode 100644
index 0000000..7132bf1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd
@@ -0,0 +1,345 @@
+@tool
+extends Window
+
+# =============================================================================
+# Symbol Navigator - Exclude Directories Dialog
+# Author: kyros
+# Configure directories to exclude from symbol search
+# =============================================================================
+
+# UI components
+@export var header_label : Label = null
+@export var directories_edit : TextEdit = null
+@export var preset_container : VBoxContainer = null
+@export var save_button : Button = null
+@export var cancel_button : Button = null
+@export var validation_label : Label = null
+
+# Preset buttons
+var godot_button : Button = null
+var git_button : Button = null
+var import_button : Button = null
+var exports_button : Button = null
+var mono_button : Button = null
+
+# Signal emitted when directories are saved
+signal directories_saved(directories: Array[String])
+
+# Data
+var _current_directories : Array[String] = []
+var _preset_directories = {
+ ".godot": "Godot 4+ engine cache and settings",
+ ".git": "Git version control files",
+ ".import": "Godot 3.x import cache files",
+ "exports": "Export output directory",
+ ".mono": "C# Mono build files and cache",
+ "__pycache__": "Python cache files",
+ ".vs": "Visual Studio files",
+ ".vscode": "VS Code settings",
+ "bin": "Binary output files",
+ "obj": "Object files from compilation"
+}
+
+func _ready() -> void:
+ # Initialize UI components
+ _find_ui_components()
+ _validate_ui_components()
+ _connect_signals()
+
+ # Initialize validation label
+ if validation_label:
+ validation_label.text = ""
+ validation_label.modulate = Color.WHITE
+
+ # Setup dialog display
+ _setup_dialog_display()
+
+func _find_ui_components() -> void:
+ """Find and assign UI components"""
+ header_label = find_child("HeaderLabel") as Label
+ directories_edit = find_child("DirectoriesEdit") as TextEdit
+ preset_container = find_child("PresetContainer") as VBoxContainer
+ save_button = find_child("SaveButton") as Button
+ cancel_button = find_child("CancelButton") as Button
+ validation_label = find_child("ValidationLabel") as Label
+
+ # Find preset buttons
+ godot_button = find_child("GodotButton") as Button
+ git_button = find_child("GitButton") as Button
+ import_button = find_child("ImportButton") as Button
+ exports_button = find_child("ExportsButton") as Button
+ mono_button = find_child("MonoButton") as Button
+
+func _validate_ui_components() -> void:
+ """Validate that all UI components were found"""
+ var missing_components = []
+
+ if not header_label:
+ missing_components.append("HeaderLabel")
+ if not directories_edit:
+ missing_components.append("DirectoriesEdit")
+ if not save_button:
+ missing_components.append("SaveButton")
+ if not cancel_button:
+ missing_components.append("CancelButton")
+
+ if not missing_components.is_empty():
+ push_warning("[Exclude Dirs Dialog] Missing UI components: " + str(missing_components))
+
+func _connect_signals() -> void:
+ """Connect button signals"""
+ if save_button:
+ save_button.pressed.connect(_on_save_pressed)
+ if cancel_button:
+ cancel_button.pressed.connect(_on_cancel_pressed)
+ if directories_edit:
+ directories_edit.text_changed.connect(_on_text_changed)
+
+ # Connect preset buttons
+ if godot_button:
+ godot_button.pressed.connect(_on_preset_pressed.bind(".godot"))
+ if git_button:
+ git_button.pressed.connect(_on_preset_pressed.bind(".git"))
+ if import_button:
+ import_button.pressed.connect(_on_preset_pressed.bind(".import"))
+ if exports_button:
+ exports_button.pressed.connect(_on_preset_pressed.bind("exports"))
+ if mono_button:
+ mono_button.pressed.connect(_on_preset_pressed.bind(".mono"))
+
+func set_excluded_directories(directories: Array[String]) -> void:
+ """Set the current excluded directories"""
+ _current_directories = directories.duplicate()
+
+ if directories_edit:
+ directories_edit.text = "\n".join(directories)
+
+ _validate_directories()
+
+func get_excluded_directories() -> Array[String]:
+ """Get the currently configured directories"""
+ if not directories_edit:
+ return _current_directories
+
+ var directories: Array[String] = []
+ var lines = directories_edit.text.split("\n")
+
+ for line in lines:
+ var trimmed = line.strip_edges()
+ if not trimmed.is_empty():
+ directories.append(trimmed)
+
+ return directories
+
+func _on_save_pressed() -> void:
+ """Handle save button press"""
+ var directories = get_excluded_directories()
+
+ # Validate directories before saving
+ if not _validate_directories():
+ return
+
+ # Emit signal with the directories
+ directories_saved.emit(directories)
+
+ # Close dialog
+ hide()
+ queue_free()
+
+func _on_cancel_pressed() -> void:
+ """Handle cancel button press"""
+ hide()
+ queue_free()
+
+func _on_close_requested() -> void:
+ """Handle window close request"""
+ _on_cancel_pressed()
+
+func _on_text_changed() -> void:
+ """Handle text changes in directories edit"""
+ _validate_directories()
+
+func _on_preset_pressed(preset_dir: String) -> void:
+ """Handle preset button press"""
+ if not directories_edit:
+ return
+
+ var current_text = directories_edit.text.strip_edges()
+ var lines = current_text.split("\n") if not current_text.is_empty() else []
+
+ # Check if directory is already in the list
+ var already_exists = false
+ for line in lines:
+ if line.strip_edges() == preset_dir:
+ already_exists = true
+ break
+
+ if not already_exists:
+ # Add the preset directory
+ if not current_text.is_empty() and not current_text.ends_with("\n"):
+ directories_edit.text += "\n"
+ directories_edit.text += preset_dir
+
+ # Show feedback
+ _show_validation_message("Added: " + preset_dir, "success")
+ else:
+ # Show feedback that it already exists
+ _show_validation_message("Already exists: " + preset_dir, "warning")
+
+ # Trigger validation
+ _validate_directories()
+
+func _validate_directories() -> bool:
+ """Validate the directory entries"""
+ if not directories_edit:
+ return true
+
+ var directories = get_excluded_directories()
+ var issues = []
+ var warnings = []
+
+ # Check for empty entries (already filtered out in get_excluded_directories())
+
+ # Check for duplicates
+ var seen_dirs = []
+ for dir in directories:
+ if dir in seen_dirs:
+ issues.append("Duplicate: " + dir)
+ else:
+ seen_dirs.append(dir)
+
+ # Check for invalid directory names
+ for dir in directories:
+ if not _is_valid_directory_name(dir):
+ issues.append("Invalid name: " + dir)
+ elif _is_system_directory(dir):
+ warnings.append("System directory: " + dir)
+
+ # Update validation display
+ if not issues.is_empty():
+ _show_validation_message("Issues: " + ", ".join(issues), "error")
+ if save_button:
+ save_button.disabled = true
+ return false
+ elif not warnings.is_empty():
+ _show_validation_message("Warnings: " + ", ".join(warnings), "warning")
+ if save_button:
+ save_button.disabled = false
+ return true
+ else:
+ var count = directories.size()
+ _show_validation_message("✓ %d directories configured" % count, "success")
+ if save_button:
+ save_button.disabled = false
+ return true
+
+func _is_valid_directory_name(dir_name: String) -> bool:
+ """Check if a directory name is valid"""
+ if dir_name.is_empty():
+ return false
+
+ # Check for invalid characters (basic check)
+ var invalid_chars = ["<", ">", ":", "\"", "|", "?", "*"]
+ for char in invalid_chars:
+ if char in dir_name:
+ return false
+
+ # Check for reserved names (Windows)
+ var reserved_names = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"]
+ if dir_name.to_upper() in reserved_names:
+ return false
+
+ return true
+
+func _is_system_directory(dir_name: String) -> bool:
+ """Check if directory is a system directory (warning, not error)"""
+ var system_dirs = ["System", "Windows", "Program Files", "Users", "etc", "usr", "var", "tmp"]
+ return dir_name in system_dirs
+
+func _show_validation_message(message: String, type: String = "info") -> void:
+ """Show validation message with appropriate color"""
+ if not validation_label:
+ return
+
+ validation_label.text = message
+
+ match type:
+ "success":
+ validation_label.modulate = Color(0.2, 0.8, 0.2, 1.0) # Green
+ "warning":
+ validation_label.modulate = Color(1.0, 0.8, 0.2, 1.0) # Orange
+ "error":
+ validation_label.modulate = Color(0.9, 0.2, 0.2, 1.0) # Red
+ _:
+ validation_label.modulate = Color.WHITE
+
+func _input(event: InputEvent) -> void:
+ """Handle keyboard shortcuts"""
+ if not visible:
+ return
+
+ if event is InputEventKey and event.pressed:
+ match event.keycode:
+ KEY_ENTER:
+ # Ctrl+Enter saves
+ if event.ctrl_pressed:
+ if save_button and not save_button.disabled:
+ _on_save_pressed()
+ get_viewport().set_input_as_handled()
+ KEY_ESCAPE:
+ # Escape key cancels
+ _on_cancel_pressed()
+ get_viewport().set_input_as_handled()
+
+func _setup_dialog_display() -> void:
+ """Setup dialog size and position for proper display"""
+ # Get optimal size for dialog
+ var optimal_size = _get_optimal_dialog_size()
+ size = optimal_size
+
+ # Center the dialog manually
+ _center_dialog()
+
+func _get_optimal_dialog_size() -> Vector2i:
+ """Calculate optimal dialog size based on content and DPI"""
+ # Base size for exclude dirs dialog
+ var base_size = Vector2i(600, 450)
+
+ # Get screen info for DPI awareness
+ var screen = DisplayServer.screen_get_size()
+ var dpi_scale = DisplayServer.screen_get_scale()
+
+ # Apply DPI scaling if needed
+ if dpi_scale > 1.0:
+ base_size = Vector2i(
+ int(base_size.x * min(dpi_scale, 1.5)),
+ int(base_size.y * min(dpi_scale, 1.5))
+ )
+
+ # Ensure minimum size
+ base_size.x = max(base_size.x, 500)
+ base_size.y = max(base_size.y, 400)
+
+ # Ensure it fits on screen (leave 100px margin)
+ base_size.x = min(base_size.x, screen.x - 100)
+ base_size.y = min(base_size.y, screen.y - 100)
+
+ return base_size
+
+func _center_dialog() -> void:
+ """Manually center the dialog on screen"""
+ var screen_size = DisplayServer.screen_get_size()
+ var dialog_size = size
+
+ # Calculate centered position
+ var centered_pos = Vector2i(
+ (screen_size.x - dialog_size.x) / 2,
+ (screen_size.y - dialog_size.y) / 2
+ )
+
+ # Ensure dialog stays on screen
+ centered_pos.x = max(0, centered_pos.x)
+ centered_pos.y = max(0, centered_pos.y)
+
+ # Set position
+ position = centered_pos
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd.uid b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd.uid
new file mode 100644
index 0000000..061e421
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd.uid
@@ -0,0 +1 @@
+uid://cpsjg2ybiew8s
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.tscn b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.tscn
new file mode 100644
index 0000000..f2e68ee
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.tscn
@@ -0,0 +1,100 @@
+[gd_scene load_steps=2 format=3 uid="uid://drnd7lnoqdl7n"]
+
+[ext_resource type="Script" uid="uid://cpsjg2ybiew8s" path="res://addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.gd" id="1_exclude_script"]
+
+[node name="ExcludeDirsDialog" type="Window" node_paths=PackedStringArray("header_label", "directories_edit", "preset_container", "save_button", "cancel_button", "validation_label")]
+title = "Configure Excluded Directories"
+position = Vector2i(1470, 742)
+size = Vector2i(900, 675)
+exclusive = true
+script = ExtResource("1_exclude_script")
+header_label = NodePath("MarginContainer/MainContainer/HeaderLabel")
+directories_edit = NodePath("MarginContainer/MainContainer/DirectoriesEdit")
+preset_container = NodePath("MarginContainer/MainContainer/PresetContainer")
+save_button = NodePath("MarginContainer/MainContainer/ButtonContainer/SaveButton")
+cancel_button = NodePath("MarginContainer/MainContainer/ButtonContainer/CancelButton")
+validation_label = NodePath("MarginContainer/MainContainer/ValidationLabel")
+
+[node name="MarginContainer" type="MarginContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_constants/margin_left = 10
+theme_override_constants/margin_top = 10
+theme_override_constants/margin_right = 10
+theme_override_constants/margin_bottom = 10
+
+[node name="MainContainer" type="VBoxContainer" parent="MarginContainer"]
+layout_mode = 2
+theme_override_constants/separation = 10
+
+[node name="HeaderLabel" type="Label" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+text = "Enter directory names to exclude from search (one per line):"
+autowrap_mode = 3
+
+[node name="DirectoriesEdit" type="TextEdit" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+placeholder_text = "Example:
+.godot
+.git
+.import
+exports
+.mono"
+wrap_mode = 1
+
+[node name="ValidationLabel" type="Label" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+vertical_alignment = 1
+autowrap_mode = 3
+
+[node name="PresetContainer" type="VBoxContainer" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+size_flags_vertical = 4
+
+[node name="PresetLabel" type="Label" parent="MarginContainer/MainContainer/PresetContainer"]
+layout_mode = 2
+text = "Quick Add Common Directories:"
+
+[node name="PresetButtonsContainer" type="HBoxContainer" parent="MarginContainer/MainContainer/PresetContainer"]
+layout_mode = 2
+theme_override_constants/separation = 5
+
+[node name="GodotButton" type="Button" parent="MarginContainer/MainContainer/PresetContainer/PresetButtonsContainer"]
+layout_mode = 2
+text = ".godot"
+
+[node name="GitButton" type="Button" parent="MarginContainer/MainContainer/PresetContainer/PresetButtonsContainer"]
+layout_mode = 2
+text = ".git"
+
+[node name="ImportButton" type="Button" parent="MarginContainer/MainContainer/PresetContainer/PresetButtonsContainer"]
+layout_mode = 2
+text = ".import"
+
+[node name="ExportsButton" type="Button" parent="MarginContainer/MainContainer/PresetContainer/PresetButtonsContainer"]
+layout_mode = 2
+text = "exports"
+
+[node name="MonoButton" type="Button" parent="MarginContainer/MainContainer/PresetContainer/PresetButtonsContainer"]
+layout_mode = 2
+text = ".mono"
+
+[node name="ButtonContainer" type="HBoxContainer" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+size_flags_vertical = 4
+theme_override_constants/separation = 10
+alignment = 2
+
+[node name="SaveButton" type="Button" parent="MarginContainer/MainContainer/ButtonContainer"]
+layout_mode = 2
+text = "Save"
+
+[node name="CancelButton" type="Button" parent="MarginContainer/MainContainer/ButtonContainer"]
+layout_mode = 2
+text = "Cancel"
+
+[connection signal="close_requested" from="." to="." method="_on_close_requested"]
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references.tscn b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references.tscn
new file mode 100644
index 0000000..134c5ab
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references.tscn
@@ -0,0 +1,378 @@
+[gd_scene load_steps=2 format=3 uid="uid://b76nmixqh7tom"]
+
+[sub_resource type="GDScript" id="GDScript_ly0sj"]
+script/source = "@tool
+extends Window
+# =============================================================================
+# Author: GodotIDE Team
+# Symbol Navigator - Find References Window
+#
+# UI for displaying symbol references across the project
+# =============================================================================
+
+@export var search_bar : LineEdit = null
+@export var results_tree : Tree = null
+@export var status_label : Label = null
+@export var search_button : Button = null
+@export var close_button : Button = null
+
+var _current_symbol : String = \"\"
+var _search_results : Array[Dictionary] = []
+
+func _ready() -> void:
+ # Set up window
+ title = \"Find References\"
+ size = Vector2(800, 600)
+
+ # Apply editor theme
+ var control : Control = EditorInterface.get_base_control()
+ if control:
+ get_child(0).add_theme_stylebox_override(&\"panel\", control.get_theme_stylebox(&\"panel\", &\"\"))
+
+ # Connect window close signal
+ close_requested.connect(_on_close_pressed)
+
+ # Connect signals
+ if search_button:
+ search_button.pressed.connect(_on_search_pressed)
+ if close_button:
+ close_button.pressed.connect(_on_close_pressed)
+ if search_bar:
+ search_bar.text_submitted.connect(_on_search_submitted)
+ if results_tree:
+ results_tree.item_activated.connect(_on_item_activated)
+
+ # Set up tree
+ if results_tree:
+ results_tree.set_column_titles_visible(true)
+ results_tree.set_column_title(0, \"File\")
+ results_tree.set_column_title(1, \"Line\")
+ results_tree.set_column_title(2, \"Context\")
+ results_tree.columns = 3
+
+func search_symbol(symbol: String) -> void:
+ _current_symbol = symbol
+ if search_bar:
+ search_bar.text = symbol
+ _perform_search()
+
+func _on_search_pressed() -> void:
+ if search_bar:
+ _current_symbol = search_bar.text
+ _perform_search()
+
+func _on_search_submitted(text: String) -> void:
+ _current_symbol = text
+ _perform_search()
+
+func _on_close_pressed() -> void:
+ hide()
+
+func _perform_search() -> void:
+ if _current_symbol.is_empty():
+ _update_status(\"Please enter a symbol to search\")
+ return
+
+ _update_status(\"Searching for references...\")
+ _clear_results()
+
+ # Perform the actual search
+ _search_in_project()
+
+ # Update UI
+ _display_results()
+ _update_status(\"Found %d references\" % _search_results.size())
+
+func _clear_results() -> void:
+ _search_results.clear()
+ if results_tree:
+ results_tree.clear()
+
+func _search_in_project() -> void:
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if not fs:
+ return
+
+ var root_dir = fs.get_filesystem()
+ if root_dir:
+ _search_in_directory(root_dir)
+
+func _search_in_directory(dir: EditorFileSystemDirectory) -> void:
+ # Search in files
+ for i in range(dir.get_file_count()):
+ var file_path = dir.get_file_path(i)
+ var file_type = dir.get_file_type(i)
+
+ # Only search in script files
+ if file_type == \"GDScript\" or file_path.ends_with(\".gd\"):
+ _search_in_file(file_path)
+
+ # Search in subdirectories
+ for i in range(dir.get_subdir_count()):
+ _search_in_directory(dir.get_subdir(i))
+
+func _search_in_file(file_path: String) -> void:
+ var file = FileAccess.open(file_path, FileAccess.READ)
+ if not file:
+ return
+
+ var line_number = 1
+ while not file.eof_reached():
+ var line = file.get_line()
+ var matches = _find_symbol_in_line(line, _current_symbol)
+
+ for match_pos in matches:
+ var result = {
+ \"file_path\": file_path,
+ \"line_number\": line_number,
+ \"line_content\": line.strip_edges(),
+ \"column\": match_pos
+ }
+ _search_results.append(result)
+
+ line_number += 1
+
+ file.close()
+
+func _find_symbol_in_line(line: String, symbol: String) -> Array[int]:
+ var matches: Array[int] = []
+
+ # Skip lines that are likely false positives
+ var trimmed_line = line.strip_edges()
+
+ # Skip comments (but allow commented code for reference)
+ if trimmed_line.begins_with(\"#\") and not trimmed_line.contains(\"func \"):
+ return matches
+
+ # Skip string literals (basic detection)
+ if _is_in_string_literal(line, symbol):
+ return matches
+
+ # Create regex pattern for word boundary matching
+ var regex = RegEx.new()
+ var pattern = \"\\\\b\" + _escape_regex_string(symbol) + \"\\\\b\"
+ regex.compile(pattern)
+
+ var results = regex.search_all(line)
+ for result in results:
+ var match_pos = result.get_start()
+
+ # Additional context-aware filtering
+ if _is_valid_symbol_context(line, match_pos, symbol):
+ matches.append(match_pos)
+
+ return matches
+
+# Check if symbol is inside a string literal
+func _is_in_string_literal(line: String, symbol: String) -> bool:
+ var in_string = false
+ var in_triple_string = false
+ var quote_char = \"\"
+
+ # Simple string detection (not perfect but catches most cases)
+ for i in range(line.length()):
+ var char = line[i]
+
+ # Handle triple quotes
+ if i < line.length() - 2:
+ var triple = line.substr(i, 3)
+ if triple == '\"\"\"' or triple == \"'''\":
+ if not in_string:
+ in_triple_string = not in_triple_string
+ quote_char = triple[0]
+ continue
+
+ # Handle single/double quotes
+ if char == '\"' or char == \"'\":
+ if not in_triple_string:
+ if not in_string:
+ in_string = true
+ quote_char = char
+ elif char == quote_char:
+ in_string = false
+ quote_char = \"\"
+
+ # If we find the symbol and we're currently in a string, it's probably a false positive
+ return (in_string or in_triple_string) and symbol in line
+
+# Check if the symbol appears in a valid context
+func _is_valid_symbol_context(line: String, position: int, symbol: String) -> bool:
+ # Get context around the symbol
+ var start = max(0, position - 10)
+ var end = min(line.length(), position + symbol.length() + 10)
+ var context = line.substr(start, end - start)
+
+ # Skip if it's part of a longer identifier (additional safety)
+ if position > 0:
+ var prev_char = line[position - 1]
+ if _is_identifier_char(prev_char):
+ return false
+
+ if position + symbol.length() < line.length():
+ var next_char = line[position + symbol.length()]
+ if _is_identifier_char(next_char):
+ return false
+
+ return true
+
+func _is_identifier_char(char: String) -> bool:
+ return char.is_valid_identifier() or char == \"_\"
+
+# Escape special regex characters since Godot 4 doesn't have RegEx.escape()
+func _escape_regex_string(text: String) -> String:
+ var escaped = text
+ # Order matters - escape backslash first
+ escaped = escaped.replace(\"\\\\\", \"\\\\\\\\\")
+ escaped = escaped.replace(\".\", \"\\\\.\")
+ escaped = escaped.replace(\"^\", \"\\\\^\")
+ escaped = escaped.replace(\"$\", \"\\\\$\")
+ escaped = escaped.replace(\"*\", \"\\\\*\")
+ escaped = escaped.replace(\"+\", \"\\\\+\")
+ escaped = escaped.replace(\"?\", \"\\\\?\")
+ escaped = escaped.replace(\"(\", \"\\\\(\")
+ escaped = escaped.replace(\")\", \"\\\\)\")
+ escaped = escaped.replace(\"[\", \"\\\\[\")
+ escaped = escaped.replace(\"]\", \"\\\\]\")
+ escaped = escaped.replace(\"{\", \"\\\\{\")
+ escaped = escaped.replace(\"}\", \"\\\\}\")
+ escaped = escaped.replace(\"|\", \"\\\\|\")
+ return escaped
+
+func _display_results() -> void:
+ if not results_tree:
+ return
+
+ results_tree.clear()
+ var root = results_tree.create_item()
+ root.set_text(0, \"References (%d)\" % _search_results.size())
+
+ var file_groups = {}
+
+ # Group results by file
+ for result in _search_results:
+ var file_path = result[\"file_path\"]
+ if not file_groups.has(file_path):
+ file_groups[file_path] = []
+ file_groups[file_path].append(result)
+
+ # Create tree structure
+ for file_path in file_groups.keys():
+ var file_item = root.create_child()
+ var file_name = file_path.get_file()
+ var relative_path = file_path.replace(\"res://\", \"\")
+
+ file_item.set_text(0, file_name)
+ file_item.set_text(1, \"\")
+ file_item.set_text(2, relative_path)
+ file_item.set_metadata(0, {\"type\": \"file\", \"path\": file_path})
+
+ # Add individual references
+ for result in file_groups[file_path]:
+ var ref_item = file_item.create_child()
+ ref_item.set_text(0, \"\")
+ ref_item.set_text(1, str(result[\"line_number\"]))
+ ref_item.set_text(2, result[\"line_content\"])
+ ref_item.set_metadata(0, result)
+
+func _on_item_activated() -> void:
+ var selected = results_tree.get_selected()
+ if not selected:
+ return
+
+ var metadata = selected.get_metadata(0)
+ if not metadata:
+ return
+
+ # Navigate to the reference
+ if metadata.has(\"file_path\") and metadata.has(\"line_number\"):
+ _navigate_to_reference(metadata[\"file_path\"], metadata[\"line_number\"], metadata.get(\"column\", 0))
+
+func _navigate_to_reference(file_path: String, line_number: int, column: int) -> void:
+ # Open the file in the script editor
+ var script = ResourceLoader.load(file_path)
+ if script and script is Script:
+ EditorInterface.edit_script(script)
+
+ # Navigate to specific line
+ var script_editor = EditorInterface.get_script_editor()
+ if script_editor:
+ script_editor.goto_line(line_number - 1)
+
+ # Focus on the specific column if possible
+ var current_editor = script_editor.get_current_editor()
+ if current_editor:
+ var code_edit : CodeEdit = current_editor.get_base_editor()
+ if code_edit:
+ code_edit.set_caret_line(line_number - 1)
+ code_edit.set_caret_column(column)
+
+func _update_status(message: String) -> void:
+ if status_label:
+ status_label.text = message
+
+
+func _on_close_requested() -> void:
+ _on_close_pressed()
+
+
+func _on_focus_exited() -> void:
+ _on_close_pressed()
+"
+
+[node name="Window" type="Window" node_paths=PackedStringArray("search_bar", "results_tree", "status_label", "search_button", "close_button")]
+title = "Find References"
+position = Vector2i(0, 36)
+size = Vector2i(800, 600)
+exclusive = true
+script = SubResource("GDScript_ly0sj")
+search_bar = NodePath("VBoxContainer/SearchBar/SearchBar")
+results_tree = NodePath("VBoxContainer/MainContent/Tree")
+status_label = NodePath("VBoxContainer/Status/Label")
+search_button = NodePath("VBoxContainer/SearchBar/Button")
+close_button = NodePath("VBoxContainer/Status/Button")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+
+[node name="SearchBar" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/SearchBar"]
+layout_mode = 2
+text = "Search for:"
+
+[node name="SearchBar" type="LineEdit" parent="VBoxContainer/SearchBar"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="Button" type="Button" parent="VBoxContainer/SearchBar"]
+layout_mode = 2
+text = "Search"
+
+[node name="MainContent" type="HSplitContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="Tree" type="Tree" parent="VBoxContainer/MainContent"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_stretch_ratio = 0.0
+columns = 3
+column_titles_visible = true
+
+[node name="Status" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 4
+
+[node name="Label" type="Label" parent="VBoxContainer/Status"]
+layout_mode = 2
+text = "Ready"
+
+[node name="Button" type="Button" parent="VBoxContainer/Status"]
+layout_mode = 2
+text = "Close"
+
+[connection signal="close_requested" from="." to="." method="_on_close_requested"]
+[connection signal="focus_exited" from="." to="." method="_on_focus_exited"]
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd
new file mode 100644
index 0000000..bf03cf6
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd
@@ -0,0 +1,1123 @@
+@tool
+extends Control
+# =============================================================================
+# Symbol Navigator - Find References Panel
+# Author: kyros
+# Bottom panel UI for displaying symbol references with navigation and code preview
+# =============================================================================
+
+@export var search_bar : LineEdit = null
+@export var results_tree : Tree = null
+@export var status_label : Label = null
+@export var search_button : Button = null
+@export var clear_button : Button = null
+@export var code_header : Label = null
+@export var code_display : RichTextLabel = null
+@export var case_sensitive_check : CheckBox = null
+@export var match_mode_option : OptionButton = null
+@export var exclude_dirs_button : Button = null
+@export var highlight_style_option : OptionButton = null
+
+enum MatchMode {
+ WORD_BOUNDARY, # Word boundary matching (default)
+ EXACT, # Hole line exact match
+ STARTS_WITH, # Start matching
+ CONTAINS, # Include match
+ ENDS_WITH # end match
+}
+
+var _current_symbol : String = ""
+var _search_results : Array[Dictionary] = []
+var _case_sensitive : bool = false
+var _match_mode : MatchMode = MatchMode.WORD_BOUNDARY
+var _excluded_directories : Array[String] = []
+var _highlight_style : String = "dots"
+var _highlight_prefix : String = "·"
+var _highlight_suffix : String = "·"
+
+func _ready() -> void:
+ # Initialize components and connections
+ _ensure_components_initialized()
+
+ # Apply editor theme for integration
+ var editor_control : Control = EditorInterface.get_base_control()
+ if editor_control:
+ var theme = editor_control.get_theme()
+ if theme:
+ set_theme(theme)
+
+ # Connect UI signals
+ if search_button:
+ search_button.pressed.connect(_on_search_pressed)
+ if clear_button:
+ clear_button.pressed.connect(_on_clear_pressed)
+ if search_bar:
+ search_bar.text_submitted.connect(_on_search_submitted)
+ search_bar.text_changed.connect(_on_search_text_changed)
+ if results_tree:
+ results_tree.item_activated.connect(_on_item_activated)
+ results_tree.item_selected.connect(_on_item_selected)
+ if case_sensitive_check:
+ case_sensitive_check.toggled.connect(_on_case_sensitive_toggled)
+ if match_mode_option:
+ match_mode_option.item_selected.connect(_on_match_mode_changed)
+ _setup_match_mode_options()
+ if exclude_dirs_button:
+ exclude_dirs_button.pressed.connect(_on_exclude_dirs_pressed)
+ if highlight_style_option:
+ highlight_style_option.item_selected.connect(_on_highlight_style_changed)
+ _setup_highlight_style_options()
+
+ # Configure tree columns
+ if results_tree:
+ results_tree.set_column_titles_visible(true)
+ results_tree.set_column_title(0, "File / Reference")
+ results_tree.set_column_title(1, "Line")
+ results_tree.columns = 2
+ results_tree.set_column_expand_ratio(0, 3.0)
+ results_tree.set_column_expand_ratio(1, 1.0)
+
+ # Initialize UI state
+ _update_results_info("Enter a symbol to search")
+ _clear_code_display()
+
+ # Load saved configuration
+ _load_configuration()
+
+func _load_configuration() -> void:
+ """Load saved configuration settings"""
+ # Load case sensitivity setting
+ var saved_case_sensitive = IDE.get_config("symbol_navigator", "case_sensitive") #if not exist config, return a null
+ if saved_case_sensitive is bool:
+ _case_sensitive = saved_case_sensitive
+ if case_sensitive_check:
+ case_sensitive_check.set_pressed_no_signal(_case_sensitive)
+
+ # Load match mode setting
+ var saved_match_mode = IDE.get_config("symbol_navigator", "match_mode")
+ if null != saved_match_mode:
+ _match_mode = saved_match_mode as MatchMode
+ if match_mode_option:
+ match_mode_option.select(saved_match_mode)
+
+ # Load excluded directories
+ var saved_excluded_dirs = IDE.get_config("symbol_navigator", "excluded_directories")
+ if null != saved_excluded_dirs:
+ _excluded_directories = saved_excluded_dirs
+
+ # Load highlight style setting
+ var saved_highlight_style = IDE.get_config("symbol_navigator", "highlight_style")
+ _highlight_style = saved_highlight_style if saved_highlight_style else "dots"
+ _update_highlight_markers()
+ if highlight_style_option:
+ highlight_style_option.select(_get_style_index(_highlight_style))
+
+func _ensure_components_initialized() -> void:
+ """Ensure all exported components are properly initialized"""
+ if not search_bar:
+ search_bar = _find_component_robust("SearchBar", LineEdit)
+ if not results_tree:
+ results_tree = _find_component_robust("ResultsTree", Tree)
+ if not status_label:
+ status_label = _find_component_robust("StatusLabel", Label)
+ if not search_button:
+ search_button = _find_component_robust("SearchButton", Button)
+ if not clear_button:
+ clear_button = _find_component_robust("ClearButton", Button)
+ if not code_header:
+ code_header = _find_component_robust("CodeHeader", Label)
+ if not code_display:
+ code_display = _find_component_robust("CodeDisplay", TextEdit)
+ if not case_sensitive_check:
+ case_sensitive_check = _find_component_robust("CaseSensitiveCheck", CheckBox)
+ if not match_mode_option:
+ match_mode_option = _find_component_robust("MatchModeOption", OptionButton)
+ if not exclude_dirs_button:
+ exclude_dirs_button = _find_component_robust("ExcludeDirsButton", Button)
+ if not highlight_style_option:
+ highlight_style_option = _find_component_robust("HighlightStyleOption", OptionButton)
+
+ # Only log if critical components are missing
+ var missing_components = []
+ if not search_bar: missing_components.append("search_bar")
+ if not results_tree: missing_components.append("results_tree")
+ if not status_label: missing_components.append("status_label")
+
+ if not missing_components.is_empty():
+ print("Error: Missing critical components: %s" % ", ".join(missing_components))
+
+
+func _find_component_robust(component_name: String, component_type) -> Node:
+ """Robust component finder using multiple strategies"""
+ var found_component: Node = null
+
+ # Strategy 1: Direct NodePath lookup
+ var expected_paths = _get_expected_paths(component_name)
+ for path in expected_paths:
+ found_component = get_node_or_null(path)
+ if found_component and _is_correct_type(found_component, component_type):
+ return found_component
+
+ # Strategy 2: find_child() search
+ found_component = find_child(component_name, true, false)
+ if found_component and _is_correct_type(found_component, component_type):
+ return found_component
+
+ # Strategy 3: Recursive search by type and name
+ found_component = _recursive_find_by_type_and_name(self, component_type, component_name)
+ if found_component:
+ return found_component
+
+ # Strategy 4: Recursive search by type only (first match)
+ found_component = _recursive_find_by_type(self, component_type)
+ if found_component:
+ return found_component
+
+ # Strategy 5: Partial name matching
+ found_component = _recursive_find_by_partial_name(self, component_name.to_lower())
+ if found_component and _is_correct_type(found_component, component_type):
+ return found_component
+
+ return null
+
+func _is_correct_type(node: Node, expected_type) -> bool:
+ """Check if a node is of the expected type"""
+ # Use multiple type checking methods for robustness
+ if expected_type == LineEdit:
+ return node is LineEdit
+ elif expected_type == Tree:
+ return node is Tree
+ elif expected_type == Label:
+ return node is Label
+ elif expected_type == Button:
+ return node is Button
+ elif expected_type == TextEdit:
+ return node is TextEdit
+ elif expected_type == CheckBox:
+ return node is CheckBox
+ elif expected_type == OptionButton:
+ return node is OptionButton
+ elif expected_type == RichTextLabel:
+ return node is RichTextLabel
+ else:
+ # Fallback to class name comparison
+ return node.get_class() == str(expected_type).get_slice(":", 0)
+
+func _get_expected_paths(component_name: String) -> Array[String]:
+ """Get all possible paths for a component"""
+ var paths: Array[String] = []
+
+ # Add the standard expected paths
+ match component_name:
+ "SearchBar":
+ paths.append("MainContainer/SearchSection/SearchBar")
+ "ResultsTree":
+ paths.append("MainContainer/MainContent/LeftPanel/ResultsTree")
+ "StatusLabel":
+ paths.append("MainContainer/StatusSection/StatusLabel")
+ "SearchButton":
+ paths.append("MainContainer/SearchSection/SearchButton")
+ "ClearButton":
+ paths.append("MainContainer/StatusSection/ClearButton")
+ "CodeHeader":
+ paths.append("MainContainer/MainContent/RightPanel/CodeHeader")
+ "CodeDisplay":
+ paths.append("MainContainer/MainContent/RightPanel/CodeDisplay")
+ "CaseSensitiveCheck":
+ paths.append("MainContainer/SearchSection/CaseSensitiveCheck")
+ "MatchModeOption":
+ paths.append("MainContainer/SearchSection/MatchModeOption")
+ "ExcludeDirsButton":
+ paths.append("MainContainer/SearchSection/ExcludeDirsButton")
+ "HighlightStyleOption":
+ paths.append("MainContainer/SearchSection/HighlightStyleOption")
+
+ return paths
+
+func _recursive_find_by_type_and_name(node: Node, target_type, target_name: String) -> Node:
+ """Recursively search for a node of the specified type and name"""
+ if _is_correct_type(node, target_type) and target_name.to_lower() in node.name.to_lower():
+ return node
+
+ for child in node.get_children():
+ var result = _recursive_find_by_type_and_name(child, target_type, target_name)
+ if result:
+ return result
+
+ return null
+
+func _recursive_find_by_type(node: Node, target_type) -> Node:
+ """Recursively search for a node of the specified type"""
+ if _is_correct_type(node, target_type):
+ return node
+
+ for child in node.get_children():
+ var result = _recursive_find_by_type(child, target_type)
+ if result:
+ return result
+
+ return null
+
+func _recursive_find_by_partial_name(node: Node, partial_name: String) -> Node:
+ """Recursively search for a node with a name containing the partial string"""
+ if partial_name in node.name.to_lower():
+ return node
+
+ for child in node.get_children():
+ var result = _recursive_find_by_partial_name(child, partial_name)
+ if result:
+ return result
+
+ return null
+
+func search_symbol(symbol: String) -> void:
+ """Start a search for the given symbol and display results"""
+ _current_symbol = symbol
+ if search_bar:
+ search_bar.text = symbol
+
+ # Ensure components are ready before search
+ _ensure_components_initialized()
+
+ _perform_search()
+
+func _on_search_pressed() -> void:
+ if search_bar:
+ _current_symbol = search_bar.text.strip_edges()
+ _perform_search()
+
+func _on_search_submitted(text: String) -> void:
+ _current_symbol = text.strip_edges()
+ _perform_search()
+
+func _on_search_text_changed(text: String) -> void:
+ # Enable/disable search button based on input
+ if search_button:
+ search_button.disabled = text.strip_edges().is_empty()
+
+func _on_clear_pressed() -> void:
+ _clear_results()
+ if search_bar:
+ search_bar.clear()
+ search_bar.grab_focus()
+ _update_status("Cleared")
+ _update_results_info("Enter a symbol to search")
+
+func _on_case_sensitive_toggled(pressed: bool) -> void:
+ _case_sensitive = pressed
+ # Save setting
+ IDE.set_config("symbol_navigator", "case_sensitive", _case_sensitive)
+ # Automatically re-search if we have a current symbol
+ if not _current_symbol.is_empty():
+ _perform_search()
+
+func _on_match_mode_changed(index: int) -> void:
+ _match_mode = index as MatchMode
+ # Save setting
+ IDE.set_config("symbol_navigator", "match_mode", _match_mode)
+ # Automatically re-search if we have a current symbol
+ if not _current_symbol.is_empty():
+ _perform_search()
+
+func _on_highlight_style_changed(index: int) -> void:
+ """Handle highlight style option change"""
+ var style_names = ["dots", "brackets", "arrows", "quotes", "squares", "circles"]
+ _highlight_style = style_names[index]
+ _update_highlight_markers()
+ # Save setting
+ IDE.set_config("symbol_navigator", "highlight_style", _highlight_style)
+ # Automatically re-search if we have a current symbol
+ if not _current_symbol.is_empty():
+ _perform_search()
+
+func _update_highlight_markers() -> void:
+ """Update highlight prefix and suffix based on current style"""
+ match _highlight_style:
+ "dots":
+ _highlight_prefix = "·"
+ _highlight_suffix = "·"
+ "brackets":
+ _highlight_prefix = "("
+ _highlight_suffix = ")"
+ "arrows":
+ _highlight_prefix = "→"
+ _highlight_suffix = "←"
+ "quotes":
+ _highlight_prefix = "「"
+ _highlight_suffix = "」"
+ "squares":
+ _highlight_prefix = "▪"
+ _highlight_suffix = "▪"
+ "circles":
+ _highlight_prefix = "○"
+ _highlight_suffix = "○"
+ _:
+ _highlight_prefix = "·"
+ _highlight_suffix = "·"
+
+func _get_style_index(style_name: String) -> int:
+ """Get the index of a style name in the options"""
+ var style_names = ["dots", "brackets", "arrows", "quotes", "squares", "circles"]
+ var index = style_names.find(style_name)
+ return index if index != -1 else 0
+
+func _setup_match_mode_options() -> void:
+ if not match_mode_option:
+ return
+
+ match_mode_option.clear()
+ match_mode_option.add_item("Word Boundary")
+ match_mode_option.add_item("Exact Match")
+ match_mode_option.add_item("Starts With")
+ match_mode_option.add_item("Contains")
+ match_mode_option.add_item("Ends With")
+ match_mode_option.selected = _match_mode
+
+func _setup_highlight_style_options() -> void:
+ """Setup the highlight style option button with available styles"""
+ if not highlight_style_option:
+ return
+
+ highlight_style_option.clear()
+ highlight_style_option.add_item("Dots (·symbol·)")
+ highlight_style_option.add_item("Brackets ((symbol))")
+ highlight_style_option.add_item("Arrows (→symbol←)")
+ highlight_style_option.add_item("Quotes (「symbol」)")
+ highlight_style_option.add_item("Squares (▪symbol▪)")
+ highlight_style_option.add_item("Circles (○symbol○)")
+ highlight_style_option.selected = _get_style_index(_highlight_style)
+
+func _on_exclude_dirs_pressed() -> void:
+ """Show dialog to configure excluded directories"""
+ _show_exclude_dirs_dialog()
+
+func _show_exclude_dirs_dialog() -> void:
+ """Show dialog to configure excluded directories using dedicated scene"""
+ # Load the exclude directories dialog scene
+ var dialog_scene = load("res://addons/_Godot-IDE_/plugins/symbol_navigator/gui/exclude_dirs_dialog.tscn")
+ if not dialog_scene:
+ print("Error: Cannot load exclude_dirs_dialog.tscn")
+ _show_exclude_dirs_dialog_fallback()
+ return
+
+ # Instantiate the dialog
+ var dialog = dialog_scene.instantiate()
+ if not dialog:
+ print("Error: Cannot instantiate exclude_dirs_dialog")
+ _show_exclude_dirs_dialog_fallback()
+ return
+
+ # Set current excluded directories
+ dialog.set_excluded_directories(_excluded_directories)
+
+ # Connect the save signal
+ dialog.directories_saved.connect(_on_directories_saved)
+
+ # Add to editor interface (better than current_scene for editor plugins)
+ var editor_control = EditorInterface.get_base_control()
+ if editor_control:
+ editor_control.add_child(dialog)
+ else:
+ # Fallback to current scene
+ get_tree().current_scene.add_child(dialog)
+
+ # Show the dialog
+ dialog.popup_centered()
+
+func _on_directories_saved(directories: Array[String]) -> void:
+ """Handle directories saved from the exclude dialog"""
+ _excluded_directories = directories.duplicate()
+
+ # Save to configuration
+ IDE.set_config("symbol_navigator", "excluded_directories", _excluded_directories)
+
+ # Update status
+ var count = _excluded_directories.size()
+ _update_status("Updated excluded directories (%d configured)" % count)
+
+ # Re-search if we have a current symbol
+ if not _current_symbol.is_empty():
+ _perform_search()
+
+func _show_exclude_dirs_dialog_fallback() -> void:
+ """Fallback dialog implementation for when scene loading fails"""
+ var dialog = AcceptDialog.new()
+ dialog.title = "Configure Excluded Directories"
+ dialog.size = Vector2i(400, 300)
+
+ var vbox = VBoxContainer.new()
+ dialog.add_child(vbox)
+
+ var info_label = Label.new()
+ info_label.text = "Enter directory names to exclude from search (one per line):"
+ vbox.add_child(info_label)
+
+ var text_edit = TextEdit.new()
+ text_edit.text = "\n".join(_excluded_directories)
+ text_edit.custom_minimum_size = Vector2(380, 200)
+ vbox.add_child(text_edit)
+
+ var button_container = HBoxContainer.new()
+ vbox.add_child(button_container)
+
+ var save_button = Button.new()
+ save_button.text = "Save"
+ button_container.add_child(save_button)
+
+ var cancel_button = Button.new()
+ cancel_button.text = "Cancel"
+ button_container.add_child(cancel_button)
+
+ # Add to editor interface (improved for editor plugins)
+ var editor_control = EditorInterface.get_base_control()
+ if editor_control:
+ editor_control.add_child(dialog)
+ else:
+ get_tree().current_scene.add_child(dialog)
+
+ # Connect signals
+ save_button.pressed.connect(func():
+ _save_excluded_directories_from_fallback(text_edit.text)
+ dialog.queue_free()
+ )
+ cancel_button.pressed.connect(func():
+ dialog.queue_free()
+ )
+
+ dialog.popup_centered()
+
+func _save_excluded_directories_from_fallback(text: String) -> void:
+ """Save excluded directories from fallback dialog"""
+ var directories: Array[String] = []
+ var lines = text.split("\n")
+ for line in lines:
+ var trimmed = line.strip_edges()
+ if not trimmed.is_empty():
+ directories.append(trimmed)
+
+ # Use the same handler as the main dialog
+ _on_directories_saved(directories)
+
+func _perform_search() -> void:
+ if _current_symbol.is_empty():
+ _update_status("Please enter a symbol to search")
+ return
+
+ _update_status("Searching for references...")
+ _clear_results()
+
+ # Perform the actual search
+ _search_in_project()
+
+ # Update UI and show results
+ _display_results()
+ var result_count = _search_results.size()
+ if result_count > 0:
+ # Log successful search
+ print("Found %d references to '%s'" % [result_count, _current_symbol])
+ _update_status("Found %d references to '%s'" % [result_count, _current_symbol])
+ else:
+ _update_status("No references found for '%s'" % _current_symbol)
+
+func _clear_results() -> void:
+ _search_results.clear()
+ if results_tree:
+ results_tree.clear()
+ _clear_code_display()
+ _update_results_info("0 references found")
+
+func _search_in_project() -> void:
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if not fs:
+ return
+
+ var root_dir = fs.get_filesystem()
+ if root_dir:
+ _search_in_directory(root_dir)
+
+func _search_in_directory(dir: EditorFileSystemDirectory) -> void:
+ # Check if this directory should be excluded
+ var dir_name = dir.get_name()
+ if _should_exclude_directory(dir_name):
+ return
+
+ # Search in files
+ for i in range(dir.get_file_count()):
+ var file_path = dir.get_file_path(i)
+ var file_type = dir.get_file_type(i)
+
+ # Only search in script files
+ if file_type == "GDScript" or file_path.ends_with(".gd"):
+ _search_in_file(file_path)
+
+ # Search in subdirectories
+ for i in range(dir.get_subdir_count()):
+ _search_in_directory(dir.get_subdir(i))
+
+func _should_exclude_directory(dir_name: String) -> bool:
+ """Check if a directory should be excluded from search"""
+ for excluded_dir in _excluded_directories:
+ if dir_name == excluded_dir or dir_name.begins_with(excluded_dir + "/"):
+ return true
+ return false
+
+func _search_in_file(file_path: String) -> void:
+ var file = FileAccess.open(file_path, FileAccess.READ)
+ if not file:
+ print("Error: Cannot access file %s" % file_path.get_file())
+ return
+
+ var line_number = 1
+ while not file.eof_reached():
+ var line = file.get_line()
+ var matches = _find_symbol_in_line(line, _current_symbol)
+
+ for match_pos in matches:
+ var result = {
+ "file_path": file_path,
+ "line_number": line_number,
+ "line_content": line.strip_edges(),
+ "column": match_pos
+ }
+ _search_results.append(result)
+
+ line_number += 1
+
+ file.close()
+
+func _find_symbol_in_line(line: String, symbol: String) -> Array[int]:
+ var matches: Array[int] = []
+
+ # Skip lines that are likely false positives
+ var trimmed_line = line.strip_edges()
+
+ # Skip comments (but allow commented code for reference)
+ if trimmed_line.begins_with("#") and not trimmed_line.contains("func "):
+ return matches
+
+ # Skip string literals (basic detection) - only for word boundary mode
+ if _match_mode == MatchMode.WORD_BOUNDARY and _is_in_string_literal(line, symbol):
+ return matches
+
+ # Apply case sensitivity
+ var search_line = line if _case_sensitive else line.to_lower()
+ var search_symbol = symbol if _case_sensitive else symbol.to_lower()
+
+ # Create regex pattern based on match mode
+ var regex = RegEx.new()
+ var pattern = _get_match_pattern(search_symbol)
+ if regex.compile(pattern) != OK:
+ return matches
+
+ var results = regex.search_all(search_line)
+ for result in results:
+ var match_pos = result.get_start()
+
+ # Additional context-aware filtering (only for word boundary mode)
+ if _match_mode == MatchMode.WORD_BOUNDARY:
+ if _is_valid_symbol_context(line, match_pos, symbol):
+ matches.append(match_pos)
+ else:
+ matches.append(match_pos)
+
+ return matches
+
+func _get_match_pattern(symbol: String) -> String:
+ var escaped_symbol = _escape_regex_string(symbol)
+
+ match _match_mode:
+ MatchMode.WORD_BOUNDARY:
+ return "\\b" + escaped_symbol + "\\b"
+ MatchMode.EXACT:
+ return "^" + escaped_symbol + "$"
+ MatchMode.STARTS_WITH:
+ return "^" + escaped_symbol
+ MatchMode.CONTAINS:
+ return escaped_symbol
+ MatchMode.ENDS_WITH:
+ return escaped_symbol + "$"
+ _:
+ return "\\b" + escaped_symbol + "\\b"
+
+# Check if symbol is inside a string literal
+func _is_in_string_literal(line: String, symbol: String) -> bool:
+ var in_string = false
+ var in_triple_string = false
+ var quote_char = ""
+
+ # Simple string detection (not perfect but catches most cases)
+ for i in range(line.length()):
+ var char = line[i]
+
+ # Handle triple quotes
+ if i < line.length() - 2:
+ var triple = line.substr(i, 3)
+ if triple == '"""' or triple == "'''":
+ if not in_string:
+ in_triple_string = not in_triple_string
+ quote_char = triple[0]
+ continue
+
+ # Handle single/double quotes
+ if char == '"' or char == "'":
+ if not in_triple_string:
+ if not in_string:
+ in_string = true
+ quote_char = char
+ elif char == quote_char:
+ in_string = false
+ quote_char = ""
+
+ # If we find the symbol and we're currently in a string, it's probably a false positive
+ return (in_string or in_triple_string) and symbol in line
+
+# Check if the symbol appears in a valid context
+func _is_valid_symbol_context(line: String, position: int, symbol: String) -> bool:
+ # Get context around the symbol
+ var start = max(0, position - 10)
+ var end = min(line.length(), position + symbol.length() + 10)
+ var context = line.substr(start, end - start)
+
+ # Skip if it's part of a longer identifier (additional safety)
+ if position > 0:
+ var prev_char = line[position - 1]
+ if _is_identifier_char(prev_char):
+ return false
+
+ if position + symbol.length() < line.length():
+ var next_char = line[position + symbol.length()]
+ if _is_identifier_char(next_char):
+ return false
+
+ return true
+
+func _is_identifier_char(char: String) -> bool:
+ return char.is_valid_identifier() or char == "_"
+
+# Escape special regex characters since Godot 4 doesn't have RegEx.escape()
+func _escape_regex_string(text: String) -> String:
+ var escaped = text
+ # Order matters - escape backslash first
+ escaped = escaped.replace("\\", "\\\\")
+ escaped = escaped.replace(".", "\\.")
+ escaped = escaped.replace("^", "\\^")
+ escaped = escaped.replace("$", "\\$")
+ escaped = escaped.replace("*", "\\*")
+ escaped = escaped.replace("+", "\\+")
+ escaped = escaped.replace("?", "\\?")
+ escaped = escaped.replace("(", "\\(")
+ escaped = escaped.replace(")", "\\)")
+ escaped = escaped.replace("[", "\\[")
+ escaped = escaped.replace("]", "\\]")
+ escaped = escaped.replace("{", "\\{")
+ escaped = escaped.replace("}", "\\}")
+ escaped = escaped.replace("|", "\\|")
+ return escaped
+
+func _display_results() -> void:
+ # Ensure components are initialized
+ if not results_tree:
+ _ensure_components_initialized()
+
+ if not results_tree:
+ print("Error: results_tree component not found")
+ _display_results_fallback()
+ return
+
+ # Clear the tree and code display
+ results_tree.clear()
+ _clear_code_display()
+
+ if _search_results.is_empty():
+ _update_results_info("No references found")
+ return
+
+ # Create root item (hidden)
+ var root = results_tree.create_item()
+ var file_groups = {}
+
+ # Group results by file
+ for result in _search_results:
+ var file_path = result["file_path"]
+ if not file_groups.has(file_path):
+ file_groups[file_path] = []
+ file_groups[file_path].append(result)
+
+ # Update results info
+ var total_files = file_groups.size()
+ var total_refs = _search_results.size()
+ _update_results_info("%d references in %d files" % [total_refs, total_files])
+
+ # Create tree structure for navigation
+ for file_path in file_groups.keys():
+ var file_item = root.create_child()
+ var file_name = file_path.get_file()
+ var reference_count = file_groups[file_path].size()
+
+ # File header in navigation tree
+ file_item.set_text(0, "📁 %s (%d)" % [file_name, reference_count])
+ file_item.set_text(1, "")
+ file_item.set_metadata(0, {"type": "file", "path": file_path})
+
+ # File item styling
+ file_item.set_selectable(0, false)
+ file_item.set_custom_color(0, Color(0.8, 0.9, 1.0))
+ file_item.set_collapsed(false)
+
+ # Add individual references
+ for result in file_groups[file_path]:
+ var ref_item = file_item.create_child()
+ var line_content = result.get("line_content", "")
+ var truncated_content = _truncate_line_content(line_content, 80)
+ # Apply symbol highlighting to the tree item text using simple text markers
+ var highlighted_content = _highlight_symbol_with_text_markers(truncated_content, _current_symbol)
+ ref_item.set_text(0, " → Line %d: %s" % [result["line_number"], highlighted_content])
+ ref_item.set_text(1, str(result["line_number"]))
+ ref_item.set_metadata(0, result)
+
+ # Reference item styling
+ ref_item.set_custom_color(0, Color(0.9, 0.9, 0.9))
+ ref_item.set_custom_color(1, Color(0.8, 0.8, 0.6))
+
+ # Force tree update
+ results_tree.queue_redraw()
+
+func _clear_code_display() -> void:
+ """Clear the code display area"""
+ if code_header:
+ code_header.text = "Select a reference to view code"
+ if code_display:
+ code_display.text = ""
+ #Change to set if property placeholder_text not exist Godot 4.4.1
+ code_display.set(&"placeholder_text", "Code content will appear here...")
+
+func _update_results_info(info_text: String) -> void:
+ """Update the results info label in the left panel"""
+ # Ensure components are initialized first
+ if not results_tree:
+ _ensure_components_initialized()
+
+ # Method 1: Try using the direct NodePath first
+ var results_info_label = get_node_or_null("MainContainer/MainContent/LeftPanel/ResultsInfo")
+ if results_info_label and results_info_label is Label:
+ results_info_label.text = info_text
+ return
+
+ # Method 2: Try using parent-child relationship
+ if results_tree and results_tree.get_parent():
+ var left_panel = results_tree.get_parent()
+
+ # Try to find ResultsInfo by searching through children
+ for i in range(left_panel.get_child_count()):
+ var child = left_panel.get_child(i)
+ if child.name == "ResultsInfo" and child is Label:
+ child.text = info_text
+ return
+
+ # Log error only if all methods fail
+ print("Error: ResultsInfo label not found in UI structure")
+
+func _update_code_display(result: Dictionary) -> void:
+ """Update the right panel with code content for the selected reference"""
+ if not result.has("file_path") or not result.has("line_number"):
+ return
+
+ var file_path = result["file_path"]
+ var line_number = result["line_number"]
+ var column = result.get("column", 0)
+ var file_name = file_path.get_file()
+
+ # Update header
+ if code_header:
+ code_header.text = "%s:%d" % [file_name, line_number]
+
+ # Load and display file content with color highlighting
+ if code_display:
+ var file_content = _load_file_content(file_path)
+ if not file_content.is_empty():
+ # Show context around the line with BBCode highlighting
+ var context_content = _get_context_content_with_highlighting(file_content, line_number, column, 5)
+ code_display.text = context_content
+
+ # Enable BBCode parsing for color highlighting
+ code_display.bbcode_enabled = true
+
+func _load_file_content(file_path: String) -> String:
+ """Load the content of a file"""
+ var file = FileAccess.open(file_path, FileAccess.READ)
+ if not file:
+ print("Error: Cannot load file content for %s" % file_path.get_file())
+ return ""
+ var content = file.get_as_text()
+ file.close()
+ return content
+
+func _get_context_content_with_highlighting(file_content: String, target_line: int, target_column: int, context_lines: int) -> String:
+ """Get file content with context around the target line, with BBCode color highlighting"""
+ var lines = file_content.split("\n")
+ var start_line = max(0, target_line - context_lines - 1)
+ var end_line = min(lines.size() - 1, target_line + context_lines - 1)
+
+ var context_lines_array = []
+ for i in range(start_line, end_line + 1):
+ var line_content = lines[i]
+ var line_num = i + 1
+
+ # Add line number prefix
+ var prefix = "%3d: " % line_num
+ var formatted_line = prefix + line_content
+
+ # Apply syntax highlighting to the target line
+ if line_num == target_line:
+ formatted_line = prefix + _highlight_symbol_with_bbcode(line_content, _current_symbol)
+ # Use different background color for target line
+ formatted_line = "[bgcolor=#2d2d30]" + formatted_line + "[/bgcolor]"
+
+ context_lines_array.append(formatted_line)
+
+ return "\n".join(context_lines_array)
+
+# Keep the old function for backward compatibility (if needed elsewhere)
+func _get_context_content(file_content: String, target_line: int, context_lines: int) -> String:
+ """Get file content with context around the target line, with highlighting"""
+ var lines = file_content.split("\n")
+ var start_line = max(0, target_line - context_lines - 1)
+ var end_line = min(lines.size() - 1, target_line + context_lines - 1)
+
+ var context_lines_array = []
+ for i in range(start_line, end_line + 1):
+ var line_content = lines[i]
+ var line_num = i + 1
+
+ # Add line number prefix and highlight target line
+ var prefix = "%3d: " % line_num
+ if line_num == target_line:
+ # Highlight the target line and symbol
+ line_content = _highlight_symbol_in_line(line_content, _current_symbol)
+ prefix = "►%3d: " % line_num
+ else:
+ prefix = " %3d: " % line_num
+
+ context_lines_array.append(prefix + line_content)
+
+ return "\n".join(context_lines_array)
+
+func _truncate_line_content(line: String, max_length: int) -> String:
+ """Truncate line content if it's too long, preserving meaningful code"""
+ if line.length() <= max_length:
+ return line
+
+ # Try to show the beginning and add ellipsis
+ var truncated = line.substr(0, max_length - 3)
+ return truncated + "..."
+
+func _highlight_symbol_with_text_markers(line: String, symbol: String) -> String:
+ """Highlight symbol in a line of code using simple text markers for Tree display"""
+ if symbol.is_empty():
+ return line
+
+ # Apply case sensitivity for the display
+ var search_line = line if _case_sensitive else line.to_lower()
+ var search_symbol = symbol if _case_sensitive else symbol.to_lower()
+
+ # Create regex pattern based on match mode
+ var regex = RegEx.new()
+ var pattern = _get_match_pattern(search_symbol)
+ if regex.compile(pattern) != OK:
+ return line
+
+ # Apply highlighting using simple text markers
+ var highlighted = line
+ var results = regex.search_all(search_line)
+
+ # Apply highlights from right to left to preserve positions
+ results.reverse()
+ for result in results:
+ var start_pos = result.get_start()
+ var end_pos = result.get_end()
+ var match_text = line.substr(start_pos, end_pos - start_pos)
+
+ # Create highlighted version with configurable text markers
+ var highlight = "%s%s%s" % [_highlight_prefix, match_text, _highlight_suffix]
+
+ highlighted = highlighted.substr(0, start_pos) + highlight + highlighted.substr(end_pos)
+
+ return highlighted
+
+func _highlight_symbol_with_bbcode(line: String, symbol: String) -> String:
+ """Highlight symbol in a line of code using BBCode formatting"""
+ if symbol.is_empty():
+ return line
+
+ # Apply case sensitivity for the display
+ var search_line = line if _case_sensitive else line.to_lower()
+ var search_symbol = symbol if _case_sensitive else symbol.to_lower()
+
+ # Create regex pattern based on match mode
+ var regex = RegEx.new()
+ var pattern = _get_match_pattern(search_symbol)
+ if regex.compile(pattern) != OK:
+ return line
+
+ # Apply highlighting using BBCode
+ var highlighted = line
+ var results = regex.search_all(search_line)
+
+ # Apply highlights from right to left to preserve positions
+ results.reverse()
+ for result in results:
+ var start_pos = result.get_start()
+ var end_pos = result.get_end()
+ var match_text = line.substr(start_pos, end_pos - start_pos)
+
+ # Create highlighted version with yellow background and bold text
+ var highlight = "[bgcolor=yellow][color=black][b]%s[/b][/color][/bgcolor]" % match_text
+
+ highlighted = highlighted.substr(0, start_pos) + highlight + highlighted.substr(end_pos)
+
+ return highlighted
+
+func _highlight_symbol_in_line(line: String, symbol: String) -> String:
+ """Highlight symbol in a line of code (legacy version with symbols)"""
+ if symbol.is_empty():
+ return line
+
+ # Use word boundary matching for better accuracy
+ var regex = RegEx.new()
+ var pattern = "\\b" + _escape_regex_string(symbol) + "\\b"
+ if regex.compile(pattern) != OK:
+ return ""
+
+ var highlighted = regex.sub(line, "%s%s%s" % [_highlight_prefix, symbol, _highlight_suffix], true)
+ return highlighted
+
+func _on_item_activated() -> void:
+ var selected = results_tree.get_selected()
+ if not selected:
+ return
+
+ var metadata = selected.get_metadata(0)
+ if not metadata or not metadata.has("file_path"):
+ return
+
+ # Navigate to the reference
+ if metadata.has("line_number"):
+ _navigate_to_reference(metadata["file_path"], metadata["line_number"], metadata.get("column", 0))
+
+func _on_item_selected() -> void:
+ var selected = results_tree.get_selected()
+ if not selected:
+ return
+
+ var metadata = selected.get_metadata(0)
+ if metadata:
+ if metadata.has("type") and metadata["type"] == "file":
+ # File item selected - show file info, clear code display
+ var file_path = metadata["path"]
+ var relative_path = file_path.replace("res://", "")
+ _update_status("📁 %s" % relative_path)
+ _clear_code_display()
+ elif metadata.has("file_path") and metadata.has("line_number"):
+ # Reference item selected - show code content and update status
+ _update_code_display(metadata)
+ var file_name = metadata["file_path"].get_file()
+ var line_num = metadata["line_number"]
+ var relative_path = metadata["file_path"].replace("res://", "")
+ _update_status("📍 Line %d in %s (%s)" % [line_num, file_name, relative_path])
+
+func _navigate_to_reference(file_path: String, line_number: int, column: int) -> void:
+ # Open the file in the script editor
+ var script = ResourceLoader.load(file_path)
+ if script and script is Script:
+ EditorInterface.edit_script(script)
+
+ # Navigate to specific line
+ var script_editor = EditorInterface.get_script_editor()
+ if script_editor:
+ script_editor.goto_line(line_number - 1)
+
+ # Focus on the specific column if possible
+ var current_editor = script_editor.get_current_editor()
+ if current_editor:
+ var code_edit : CodeEdit = current_editor.get_base_editor()
+ if code_edit:
+ code_edit.set_caret_line(line_number - 1)
+ code_edit.set_caret_column(column)
+ code_edit.grab_focus()
+ else:
+ print("Error: Cannot navigate to %s:%d" % [file_path.get_file(), line_number])
+
+func _display_results_fallback() -> void:
+ """Fallback method to display results when Tree component is not available"""
+ var total_refs = _search_results.size()
+ if total_refs > 0:
+ var file_groups = {}
+ for result in _search_results:
+ var file_path = result["file_path"]
+ if not file_groups.has(file_path):
+ file_groups[file_path] = []
+ file_groups[file_path].append(result)
+
+ var total_files = file_groups.size()
+ _update_results_info("%d references in %d files" % [total_refs, total_files])
+
+ # Try to create a simple Tree component dynamically
+ _create_fallback_tree()
+ else:
+ _update_results_info("No references found")
+
+func _create_fallback_tree() -> void:
+ """Create a Tree component dynamically when the scene component is missing"""
+ # Find the left panel where the tree should be
+ var left_panel = get_node_or_null("MainContainer/MainContent/LeftPanel")
+ if not left_panel:
+ print("Error: Cannot find LeftPanel for tree creation")
+ return
+
+ # Check if there's already a Tree somewhere
+ var existing_tree = _recursive_find_by_type(left_panel, Tree)
+ if existing_tree:
+ results_tree = existing_tree
+ _display_results()
+ return
+
+ # Create a new Tree component
+ var new_tree = Tree.new()
+ new_tree.name = "FallbackResultsTree"
+ new_tree.columns = 2
+ new_tree.column_titles_visible = true
+ new_tree.hide_root = true
+ new_tree.select_mode = Tree.SELECT_SINGLE
+ new_tree.set_column_title(0, "File / Reference")
+ new_tree.set_column_title(1, "Line")
+ new_tree.set_column_expand_ratio(0, 3.0)
+ new_tree.set_column_expand_ratio(1, 1.0)
+
+ # Connect signals
+ new_tree.item_activated.connect(_on_item_activated)
+ new_tree.item_selected.connect(_on_item_selected)
+
+ # Add to the left panel
+ var insert_position = 1
+ if left_panel.get_child_count() > insert_position:
+ left_panel.add_child(new_tree)
+ left_panel.move_child(new_tree, insert_position)
+ else:
+ left_panel.add_child(new_tree)
+
+ # Set size flags to expand
+ new_tree.size_flags_vertical = Control.SIZE_EXPAND_FILL
+
+ # Update our reference and try again
+ results_tree = new_tree
+ _display_results()
+
+func _update_status(message: String) -> void:
+ if status_label:
+ status_label.text = message
+
+# Public method to show this panel in the bottom dock
+func show_and_focus() -> void:
+ show()
+ if search_bar:
+ search_bar.grab_focus()
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd.uid b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd.uid
new file mode 100644
index 0000000..b8e0a10
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd.uid
@@ -0,0 +1 @@
+uid://ccdxcm5e4uuaw
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.tscn b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.tscn
new file mode 100644
index 0000000..a8925a1
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.tscn
@@ -0,0 +1,165 @@
+[gd_scene load_steps=2 format=3 uid="uid://hsyqdgwk3rr0"]
+
+[ext_resource type="Script" uid="uid://ccdxcm5e4uuaw" path="res://addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.gd" id="1_panel_script"]
+
+[node name="FindReferencesPanel" type="Control" node_paths=PackedStringArray("search_bar", "results_tree", "status_label", "search_button", "clear_button", "code_header", "code_display", "case_sensitive_check", "match_mode_option", "exclude_dirs_button", "highlight_style_option")]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_panel_script")
+search_bar = NodePath("MainContainer/SearchSection/SearchBar")
+results_tree = NodePath("MainContainer/MainContent/LeftPanel/ResultsTree")
+status_label = NodePath("MainContainer/StatusSection/StatusLabel")
+search_button = NodePath("MainContainer/SearchSection/SearchButton")
+clear_button = NodePath("MainContainer/StatusSection/ClearButton")
+code_header = NodePath("MainContainer/MainContent/RightPanel/CodeHeader")
+code_display = NodePath("MainContainer/MainContent/RightPanel/CodeDisplay")
+case_sensitive_check = NodePath("MainContainer/SearchSection/CaseSensitiveCheck")
+match_mode_option = NodePath("MainContainer/SearchSection/MatchModeOption")
+exclude_dirs_button = NodePath("MainContainer/SearchSection/ExcludeDirsButton")
+highlight_style_option = NodePath("MainContainer/SearchSection/HighlightStyleOption")
+
+[node name="MainContainer" type="VBoxContainer" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = 4.0
+offset_top = 4.0
+offset_right = -4.0
+offset_bottom = -4.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="SearchSection" type="HBoxContainer" parent="MainContainer"]
+custom_minimum_size = Vector2(0, 36)
+layout_mode = 2
+
+[node name="SearchLabel" type="Label" parent="MainContainer/SearchSection"]
+layout_mode = 2
+text = "Search:"
+vertical_alignment = 1
+
+[node name="SearchBar" type="LineEdit" parent="MainContainer/SearchSection"]
+layout_mode = 2
+size_flags_horizontal = 3
+placeholder_text = "Enter symbol to find references..."
+select_all_on_focus = true
+
+[node name="SearchButton" type="Button" parent="MainContainer/SearchSection"]
+custom_minimum_size = Vector2(80, 0)
+layout_mode = 2
+text = "Find"
+
+[node name="ExcludeDirsButton" type="Button" parent="MainContainer/SearchSection"]
+custom_minimum_size = Vector2(80, 0)
+layout_mode = 2
+text = "Exclude Dirs"
+
+[node name="CaseSensitiveCheck" type="CheckBox" parent="MainContainer/SearchSection"]
+layout_mode = 2
+button_pressed = true
+text = "Case Sensitive"
+
+[node name="MatchModeOption" type="OptionButton" parent="MainContainer/SearchSection"]
+custom_minimum_size = Vector2(120, 0)
+layout_mode = 2
+selected = 0
+item_count = 5
+popup/item_0/text = "Word Boundary"
+popup/item_0/id = 0
+popup/item_1/text = "Exact Match"
+popup/item_1/id = 1
+popup/item_2/text = "Starts With"
+popup/item_2/id = 2
+popup/item_3/text = "Contains"
+popup/item_3/id = 3
+popup/item_4/text = "Ends With"
+popup/item_4/id = 4
+
+[node name="HighlightLabel" type="Label" parent="MainContainer/SearchSection"]
+layout_mode = 2
+text = "Highlight:"
+vertical_alignment = 1
+
+[node name="HighlightStyleOption" type="OptionButton" parent="MainContainer/SearchSection"]
+custom_minimum_size = Vector2(140, 0)
+layout_mode = 2
+selected = 3
+item_count = 6
+popup/item_0/text = "Dots (·symbol·)"
+popup/item_0/id = 0
+popup/item_1/text = "Brackets ((symbol))"
+popup/item_1/id = 1
+popup/item_2/text = "Arrows (→symbol←)"
+popup/item_2/id = 2
+popup/item_3/text = "Quotes (「symbol」)"
+popup/item_3/id = 3
+popup/item_4/text = "Squares (▪symbol▪)"
+popup/item_4/id = 4
+popup/item_5/text = "Circles (○symbol○)"
+popup/item_5/id = 5
+
+[node name="Separator1" type="HSeparator" parent="MainContainer"]
+custom_minimum_size = Vector2(0, 8)
+layout_mode = 2
+
+[node name="MainContent" type="HSplitContainer" parent="MainContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+split_offset = 300
+
+[node name="LeftPanel" type="VBoxContainer" parent="MainContainer/MainContent"]
+custom_minimum_size = Vector2(250, 0)
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="ResultsInfo" type="Label" parent="MainContainer/MainContent/LeftPanel"]
+layout_mode = 2
+text = "Enter a symbol to search"
+
+[node name="ResultsTree" type="Tree" parent="MainContainer/MainContent/LeftPanel"]
+layout_mode = 2
+size_flags_vertical = 3
+columns = 2
+column_titles_visible = true
+hide_root = true
+
+[node name="RightPanel" type="VBoxContainer" parent="MainContainer/MainContent"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="CodeHeader" type="Label" parent="MainContainer/MainContent/RightPanel"]
+layout_mode = 2
+text = "Select a reference to view code"
+
+[node name="CodeSeparator" type="HSeparator" parent="MainContainer/MainContent/RightPanel"]
+layout_mode = 2
+
+[node name="CodeDisplay" type="RichTextLabel" parent="MainContainer/MainContent/RightPanel"]
+layout_mode = 2
+size_flags_vertical = 3
+focus_mode = 2
+selection_enabled = true
+
+[node name="Separator2" type="HSeparator" parent="MainContainer"]
+custom_minimum_size = Vector2(0, 4)
+layout_mode = 2
+
+[node name="StatusSection" type="HBoxContainer" parent="MainContainer"]
+custom_minimum_size = Vector2(0, 24)
+layout_mode = 2
+
+[node name="StatusLabel" type="Label" parent="MainContainer/StatusSection"]
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Ready"
+vertical_alignment = 1
+
+[node name="ClearButton" type="Button" parent="MainContainer/StatusSection"]
+custom_minimum_size = Vector2(60, 0)
+layout_mode = 2
+text = "Clear"
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd
new file mode 100644
index 0000000..fc8d687
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd
@@ -0,0 +1,1027 @@
+@tool
+extends Window
+
+# =============================================================================
+# Symbol Navigator - Rename Dialog
+# Author: kyros
+# Rename symbols (variables, functions, classes) across the entire project
+#
+# Technical Features:
+# - Direct source replacement: Instantly update content in open editors
+# - Smart symbol matching: Precise word boundary matching with regex
+# - State preservation: Maintain user's cursor position and scroll state
+# - Quality assurance: Automatic file content verification after batch changes
+#
+# User Workflow:
+# 1. Select symbol → Press F2 (auto-preview)
+# 2. Enter new name → Press Enter or click Rename
+# 3. Files modified and editor updates in real-time ✨
+# =============================================================================
+
+# UI components
+@export var new_name_edit : LineEdit = null
+@export var scope_option : OptionButton = null
+@export var preview_tree : Tree = null
+@export var rename_button : Button = null
+@export var cancel_button : Button = null
+@export var select_all_button : Button = null
+@export var unselect_all_button : Button = null
+@export var selection_status : Label = null
+
+# Data
+var _current_symbol : String = ""
+var _rename_results : Array = []
+var _selected_items : Array = [] # Tracks which items are selected for rename
+var _file_groups : Dictionary = {} # Maps file_path to array of result indices
+var _file_selections : Dictionary = {} # Maps file_path to selection state
+var _scope_project_wide : bool = true
+
+
+func _ready() -> void:
+ # Find UI components
+ _find_ui_components()
+
+ # Validate UI components were found
+ _validate_ui_components()
+
+ # Connect signals
+ _connect_signals()
+
+ # Setup tree
+ _setup_preview_tree()
+
+ # Setup dialog display
+ _setup_dialog_display()
+
+func _find_ui_components() -> void:
+ """Find and assign UI components"""
+ new_name_edit = find_child("NewNameEdit") as LineEdit
+ scope_option = find_child("ScopeOption") as OptionButton
+ preview_tree = find_child("PreviewTree") as Tree
+ rename_button = find_child("RenameButton") as Button
+ cancel_button = find_child("CancelButton") as Button
+ select_all_button = find_child("SelectAllButton") as Button
+ unselect_all_button = find_child("UnselectAllButton") as Button
+ selection_status = find_child("SelectionStatus") as Label
+
+func _validate_ui_components() -> void:
+ """Validate that all UI components were found and report missing ones"""
+ var missing_components = []
+
+ if not new_name_edit:
+ missing_components.append("NewNameEdit")
+ if not scope_option:
+ missing_components.append("ScopeOption")
+ if not preview_tree:
+ missing_components.append("PreviewTree")
+ if not rename_button:
+ missing_components.append("RenameButton")
+ if not cancel_button:
+ missing_components.append("CancelButton")
+ if not select_all_button:
+ missing_components.append("SelectAllButton")
+ if not unselect_all_button:
+ missing_components.append("UnselectAllButton")
+ if not selection_status:
+ missing_components.append("SelectionStatus")
+
+ if missing_components.size() > 0:
+ push_warning("[Rename Dialog] Missing UI components: " + str(missing_components))
+
+func _connect_signals() -> void:
+ """Connect button signals"""
+ var connected_count = 0
+
+ if rename_button:
+ rename_button.pressed.connect(_on_rename_pressed)
+ connected_count += 1
+ if cancel_button:
+ cancel_button.pressed.connect(_on_cancel_pressed)
+ connected_count += 1
+ if scope_option:
+ scope_option.item_selected.connect(_on_scope_changed)
+ connected_count += 1
+ if new_name_edit:
+ new_name_edit.text_changed.connect(_on_text_changed)
+ connected_count += 1
+ if select_all_button:
+ select_all_button.pressed.connect(_on_select_all_pressed)
+ connected_count += 1
+ if unselect_all_button:
+ unselect_all_button.pressed.connect(_on_unselect_all_pressed)
+ connected_count += 1
+ if preview_tree:
+ preview_tree.item_edited.connect(_on_tree_item_edited)
+ connected_count += 1
+
+ if connected_count == 0:
+ push_warning("[Rename Dialog] No signals connected - UI components may not be properly initialized")
+
+
+
+func _setup_preview_tree() -> void:
+ """Setup the preview tree columns"""
+ if not preview_tree:
+ return
+
+ preview_tree.columns = 3
+ preview_tree.set_column_title(0, "✓")
+ preview_tree.set_column_title(1, "File / Location")
+ preview_tree.set_column_title(2, "Line")
+ preview_tree.set_column_expand_ratio(0, 0.3) # Checkbox column (narrow)
+ preview_tree.set_column_expand_ratio(1, 3.0) # File/Location column (wide)
+ preview_tree.set_column_expand_ratio(2, 0.7) # Line number column (narrow)
+ preview_tree.hide_root = true
+
+func set_symbol(symbol: String) -> void:
+ """Set the symbol to be renamed"""
+ _current_symbol = symbol
+
+ # Check if UI components are ready
+ if not _are_ui_components_ready():
+ # Defer the call until the next frame when components should be ready
+ call_deferred("_deferred_set_symbol", symbol)
+ return
+
+ _apply_symbol_to_ui(symbol)
+
+func _are_ui_components_ready() -> bool:
+ """Check if all required UI components are available"""
+ return (new_name_edit != null and
+ scope_option != null and
+ preview_tree != null and
+ rename_button != null and
+ cancel_button != null)
+
+func _deferred_set_symbol(symbol: String) -> void:
+ """Deferred version of set_symbol, called when UI is ready"""
+ if not _are_ui_components_ready():
+ print("[Rename Dialog] ERROR: UI components still not ready after deferral")
+ print("[Rename Dialog] new_name_edit: ", new_name_edit)
+ print("[Rename Dialog] scope_option: ", scope_option)
+ print("[Rename Dialog] preview_tree: ", preview_tree)
+ print("[Rename Dialog] rename_button: ", rename_button)
+ print("[Rename Dialog] cancel_button: ", cancel_button)
+ return
+
+ _apply_symbol_to_ui(symbol)
+
+func _apply_symbol_to_ui(symbol: String) -> void:
+ """Apply the symbol to the UI components"""
+ # Update UI
+ if new_name_edit:
+ new_name_edit.text = symbol
+ new_name_edit.select_all()
+ new_name_edit.grab_focus()
+
+ title = "Rename Symbol: " + symbol
+
+ # Automatically trigger preview when symbol is set
+ call_deferred("_auto_preview")
+
+func _auto_preview() -> void:
+ """Automatically trigger preview without changing the current name"""
+ # Verify UI components are still available
+ if not _are_ui_components_ready():
+ print("[Rename Symbol] ERROR: UI components not ready during auto-preview")
+ return
+
+ # Clear previous results
+ _rename_results.clear()
+
+ # Search for all occurrences
+ _search_symbol_occurrences()
+
+ # Display results in tree
+ _display_preview_results()
+
+ # Update status
+ if _rename_results.size() == 0:
+ print("[Rename Symbol] No occurrences found for symbol: '%s'" % _current_symbol)
+
+
+func _on_rename_pressed() -> void:
+ """Perform the actual rename operation"""
+ # Comprehensive pre-flight checks
+ if _rename_results.is_empty():
+ _show_error("No preview results. Please click Preview first.")
+ return
+
+ if not new_name_edit or not is_instance_valid(new_name_edit):
+ _show_error("Name input field not available")
+ return
+
+ var new_name = new_name_edit.text.strip_edges()
+ if new_name.is_empty():
+ _show_error("New name cannot be empty")
+ return
+
+ if new_name == _current_symbol:
+ _show_error("New name is the same as current symbol")
+ return
+
+ if _selected_items.is_empty():
+ _show_error("No items selected for rename")
+ return
+
+ # Disable the rename button to prevent double-clicks
+ if rename_button:
+ rename_button.disabled = true
+
+ # Perform batch rename with error handling
+ var success = _perform_batch_rename(new_name)
+ if not success:
+ _show_error("Rename operation failed")
+ if rename_button:
+ rename_button.disabled = false
+ return
+
+ if success:
+ # Force Godot to refresh the file system
+ _refresh_file_system()
+
+ # Verify the modifications actually took effect
+ var verification_success = _verify_modifications(new_name)
+
+ if verification_success:
+ # Success - no log needed for normal operation
+ var message = "Successfully renamed '%s' to '%s' in %d locations" % [_current_symbol, new_name, _selected_items.size()]
+ _show_success(message)
+ else:
+ # Partial failure - files may be modified but verification failed
+ var warning = "Rename completed but verification failed. Please check files manually."
+ _show_error(warning)
+
+ hide()
+ else:
+ # Complete operation failure
+ _show_error("Rename operation failed. Some files may have been modified.")
+
+ # Re-enable the rename button
+ if rename_button:
+ rename_button.disabled = false
+
+func _on_cancel_pressed() -> void:
+ """Cancel the rename operation"""
+ hide()
+
+func _on_scope_changed(index: int) -> void:
+ """Handle scope selection change"""
+ _scope_project_wide = (index == 1) # Assuming index 1 is "Entire Project"
+
+ # Re-trigger preview when scope changes
+ if not _current_symbol.is_empty():
+ call_deferred("_auto_preview")
+
+func _on_text_changed(new_text: String) -> void:
+ """Handle text changes in the name input"""
+ # Check for special characters and validate name
+ var trimmed_text = new_text.strip_edges()
+ var is_valid = _validate_symbol_name(trimmed_text)
+
+ # Enable/disable rename button based on text validity
+ if rename_button:
+ rename_button.disabled = not is_valid
+
+ # Special Character Compatibility Check
+ if not trimmed_text.is_empty() and not _is_name_compatible(trimmed_text):
+ pass # Silently validate compatibility
+
+func _input(event: InputEvent) -> void:
+ """Handle keyboard shortcuts"""
+ if not visible:
+ return
+
+ if event is InputEventKey and event.pressed:
+ match event.keycode:
+ KEY_ENTER:
+ # Enter key triggers rename if valid
+ if rename_button and not rename_button.disabled:
+ _on_rename_pressed()
+ get_viewport().set_input_as_handled()
+ KEY_ESCAPE:
+ # Escape key cancels
+ _on_cancel_pressed()
+ get_viewport().set_input_as_handled()
+ KEY_A:
+ # Ctrl+A for select all
+ if event.ctrl_pressed:
+ _on_select_all_pressed()
+ get_viewport().set_input_as_handled()
+
+func _on_select_all_pressed() -> void:
+ """Select all items for renaming"""
+ if not preview_tree:
+ return
+
+ _selected_items.clear()
+
+ # Add all valid indices to selected items
+ for i in range(_rename_results.size()):
+ _selected_items.append(i)
+
+ # Update file selections
+ for file_path in _file_groups.keys():
+ _file_selections[file_path] = true
+
+ # Update checkboxes in tree
+ _update_tree_checkboxes(true)
+ _update_selection_status()
+
+func _on_unselect_all_pressed() -> void:
+ """Unselect all items"""
+ if not preview_tree:
+ return
+
+ _selected_items.clear()
+
+ # Update file selections
+ for file_path in _file_groups.keys():
+ _file_selections[file_path] = false
+
+ # Update checkboxes in tree
+ _update_tree_checkboxes(false)
+ _update_selection_status()
+
+func _on_tree_item_edited() -> void:
+ """Handle checkbox clicks in tree (both file-level and item-level)"""
+ var edited_item = preview_tree.get_edited()
+ if not edited_item:
+ return
+
+ var metadata = edited_item.get_metadata(0)
+ if metadata == null:
+ return
+
+ var is_checked = edited_item.is_checked(0)
+
+ if metadata.has("type"):
+ if metadata["type"] == "file":
+ # Handle file-level checkbox
+ _on_file_checkbox_changed(metadata["path"], is_checked)
+ elif metadata["type"] == "item":
+ # Handle item-level checkbox
+ _on_item_checkbox_changed(metadata["index"], is_checked)
+
+ _update_selection_status()
+
+func _on_file_checkbox_changed(file_path: String, is_checked: bool) -> void:
+ """Handle file-level checkbox changes"""
+ if not _file_groups.has(file_path):
+ return
+
+ # Update file selection state
+ _file_selections[file_path] = is_checked
+
+ # Update all items in this file
+ for item_data in _file_groups[file_path]:
+ var index = item_data["index"]
+
+ if is_checked:
+ # Add to selected items if not already there
+ if index not in _selected_items:
+ _selected_items.append(index)
+ else:
+ # Remove from selected items
+ if index in _selected_items:
+ _selected_items.erase(index)
+
+ # Update the visual state of child checkboxes
+ _update_child_checkboxes(file_path, is_checked)
+
+func _on_item_checkbox_changed(index: int, is_checked: bool) -> void:
+ """Handle individual item checkbox changes"""
+ if is_checked and index not in _selected_items:
+ _selected_items.append(index)
+ elif not is_checked and index in _selected_items:
+ _selected_items.erase(index)
+
+ # Update the parent file's checkbox state based on children
+ var file_path = _rename_results[index]["file_path"]
+ _update_file_checkbox_state(file_path)
+
+func _update_tree_checkboxes(checked: bool) -> void:
+ """Update all checkboxes in the tree (both files and items)"""
+ if not preview_tree:
+ return
+
+ var root = preview_tree.get_root()
+ if not root:
+ return
+
+ _iterate_tree_items(root, func(item):
+ var metadata = item.get_metadata(0)
+ if metadata != null and metadata.has("type"):
+ # Update both file and item checkboxes
+ item.set_checked(0, checked)
+ )
+
+func _iterate_tree_items(item: TreeItem, callback: Callable) -> void:
+ """Recursively iterate through tree items"""
+ if item:
+ callback.call(item)
+ var child = item.get_first_child()
+ while child:
+ _iterate_tree_items(child, callback)
+ child = child.get_next()
+
+func _update_child_checkboxes(file_path: String, checked: bool) -> void:
+ """Update all child checkboxes for a file"""
+ if not preview_tree:
+ return
+
+ var root = preview_tree.get_root()
+ if not root:
+ return
+
+ # Find the file item and update its children
+ _iterate_tree_items(root, func(item):
+ var metadata = item.get_metadata(0)
+ if metadata != null and metadata.has("type"):
+ if metadata["type"] == "file" and metadata["path"] == file_path:
+ # Found the file item, update its children
+ var child = item.get_first_child()
+ while child:
+ child.set_checked(0, checked)
+ child = child.get_next()
+ )
+
+func _update_file_checkbox_state(file_path: String) -> void:
+ """Update file checkbox based on children selection state (tri-state logic)"""
+ if not _file_groups.has(file_path):
+ return
+
+ # Count selected items in this file
+ var selected_count = 0
+ var total_count = _file_groups[file_path].size()
+
+ for item_data in _file_groups[file_path]:
+ var index = item_data["index"]
+ if index in _selected_items:
+ selected_count += 1
+
+ # Update file checkbox state and visual appearance
+ var root = preview_tree.get_root()
+ if root:
+ _iterate_tree_items(root, func(item):
+ var metadata = item.get_metadata(0)
+ if metadata != null and metadata.has("type"):
+ if metadata["type"] == "file" and metadata["path"] == file_path:
+ if selected_count == 0:
+ # None selected
+ item.set_checked(0, false)
+ _file_selections[file_path] = false
+ elif selected_count == total_count:
+ # All selected
+ item.set_checked(0, true)
+ _file_selections[file_path] = true
+ else:
+ # Partial selection - show as checked but different visual state
+ item.set_checked(0, true)
+ _file_selections[file_path] = true
+ # In Godot, we can't easily show tri-state, but we track it logically
+ )
+
+func _update_selection_status() -> void:
+ """Update the selection status label"""
+ if not selection_status:
+ return
+
+ var total_items = _rename_results.size()
+ var selected_items = _selected_items.size()
+ var total_files = _file_groups.size()
+ var selected_files = 0
+
+ # Count how many files have at least one selected item
+ for file_path in _file_groups.keys():
+ var has_selected = false
+ for item_data in _file_groups[file_path]:
+ if item_data["index"] in _selected_items:
+ has_selected = true
+ break
+ if has_selected:
+ selected_files += 1
+
+ selection_status.text = "%d files, %d of %d items selected" % [selected_files, selected_items, total_items]
+
+func _search_symbol_occurrences() -> void:
+ """Search for all occurrences of the symbol"""
+ var fs : EditorFileSystem = EditorInterface.get_resource_filesystem()
+ if not fs:
+ return
+
+ var root_dir = fs.get_filesystem()
+ if not root_dir:
+ return
+
+
+ if _scope_project_wide:
+ _search_in_directory(root_dir)
+ else:
+ # Current file only
+ var current_script = _get_current_script_path()
+ if not current_script.is_empty():
+ _search_in_file(current_script)
+
+func _search_in_directory(dir: EditorFileSystemDirectory) -> void:
+ """Recursively search in directory"""
+ # Search files in current directory
+ for i in range(dir.get_file_count()):
+ var file_path = dir.get_file_path(i)
+ if _is_script_file(file_path):
+ _search_in_file(file_path)
+
+ # Search subdirectories
+ for i in range(dir.get_subdir_count()):
+ var subdir = dir.get_subdir(i)
+ _search_in_directory(subdir)
+
+func _search_in_file(file_path: String) -> void:
+ """Search for symbol occurrences in a single file"""
+ var file = FileAccess.open(file_path, FileAccess.READ)
+ if not file:
+ push_warning("[Rename Dialog] Cannot open file: %s" % file_path)
+ return
+
+ # Safety check for file size to prevent memory issues
+ var file_size = file.get_length()
+ if file_size > 10 * 1024 * 1024: # 10MB limit
+ push_warning("[Rename Dialog] Skipping large file: %s (%d bytes)" % [file_path, file_size])
+ file.close()
+ return
+
+ var line_number = 1
+ var max_lines = 10000 # Prevent infinite loops
+ while not file.eof_reached() and line_number <= max_lines:
+ var line = file.get_line()
+ var occurrences = _find_symbol_in_line(line, _current_symbol)
+
+ for occurrence in occurrences:
+ var result = {
+ "file_path": file_path,
+ "line_number": line_number,
+ "line_content": line.strip_edges(),
+ "column": occurrence["column"],
+ "match_start": occurrence["start"],
+ "match_end": occurrence["end"]
+ }
+ _rename_results.append(result)
+
+ line_number += 1
+
+ file.close()
+
+ if line_number > max_lines:
+ push_warning("[Rename Dialog] File too long, search truncated: %s" % file_path)
+
+func _find_symbol_in_line(line: String, symbol: String) -> Array:
+ """Find all occurrences of symbol in a line, ensuring word boundaries"""
+ var occurrences = []
+
+ var regex = RegEx.new()
+ var pattern = "\\b" + _escape_regex_string(symbol) + "\\b"
+ if regex.compile(pattern) != OK:
+ return occurrences
+
+ var search_from = 0
+ while true:
+ var result = regex.search(line, search_from)
+ if not result:
+ break
+
+ occurrences.append({
+ "column": result.get_start(),
+ "start": result.get_start(),
+ "end": result.get_end(),
+ "match": result.get_string()
+ })
+
+ search_from = result.get_end()
+
+ return occurrences
+
+func _escape_regex_string(text: String) -> String:
+ """Escape special regex characters"""
+ var special_chars = ["\\", ".", "^", "$", "*", "+", "?", "(", ")", "[", "]", "{", "}", "|"]
+ var escaped = text
+ for char in special_chars:
+ escaped = escaped.replace(char, "\\" + char)
+ return escaped
+
+func _display_preview_results() -> void:
+ """Display the preview results in the tree"""
+ if not preview_tree:
+ return
+
+ preview_tree.clear()
+ _selected_items.clear()
+ _file_groups.clear()
+ _file_selections.clear()
+
+ if _rename_results.is_empty():
+ var root = preview_tree.create_item()
+ var no_results = root.create_child()
+ no_results.set_text(1, "No occurrences found") # Column 1 (File/Location)
+ no_results.set_text(2, "") # Column 2 (Line)
+ _update_selection_status()
+ return
+
+ var root = preview_tree.create_item()
+
+ # Group by file and store in class variables
+ for i in range(_rename_results.size()):
+ var result = _rename_results[i]
+ var file_path = result["file_path"]
+ if not _file_groups.has(file_path):
+ _file_groups[file_path] = []
+ _file_groups[file_path].append({"index": i, "result": result})
+
+ # Initialize file selections (all selected by default)
+ for file_path in _file_groups.keys():
+ _file_selections[file_path] = true
+
+ # Display grouped results
+ for file_path in _file_groups.keys():
+ var file_item = root.create_child()
+ var file_name = file_path.get_file()
+
+ # Column 0: File-level checkbox (selected by default)
+ file_item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK)
+ file_item.set_checked(0, true) # Default to selected
+ file_item.set_editable(0, true)
+
+ # Column 1: File name and count
+ file_item.set_text(1, "%s (%d occurrences)" % [file_name, _file_groups[file_path].size()])
+
+ # Column 2: Empty for file headers
+ file_item.set_text(2, "")
+ file_item.set_custom_color(1, Color(0.8, 0.9, 1.0))
+
+ # Store file path as metadata for file-level checkbox handling
+ file_item.set_metadata(0, {"type": "file", "path": file_path})
+
+ # Add occurrences with checkboxes
+ for item_data in _file_groups[file_path]:
+ var result = item_data["result"]
+ var index = item_data["index"]
+
+ var ref_item = file_item.create_child()
+
+ # Column 0: Checkbox (selected by default)
+ ref_item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK)
+ ref_item.set_checked(0, true) # Default to selected
+ ref_item.set_editable(0, true)
+
+ # Column 1: File/Location info
+ var line_content = result["line_content"]
+ if line_content.length() > 60:
+ line_content = line_content.substr(0, 57) + "..."
+ ref_item.set_text(1, " → Line %d: %s" % [result["line_number"], line_content])
+
+ # Column 2: Line number
+ ref_item.set_text(2, str(result["line_number"]))
+
+ # Store the index as metadata for tracking
+ ref_item.set_metadata(0, {"type": "item", "index": index})
+
+ # Add to selected items (all selected by default)
+ _selected_items.append(index)
+
+ _update_selection_status()
+
+func _perform_batch_rename(new_name: String) -> bool:
+ """Execute batch rename operation - core functionality"""
+ var files_to_modify = {}
+
+ # Group modifications by file (only selected items)
+ for i in _selected_items:
+ if i >= 0 and i < _rename_results.size():
+ var result = _rename_results[i]
+ var file_path = result["file_path"]
+ if not files_to_modify.has(file_path):
+ files_to_modify[file_path] = []
+ files_to_modify[file_path].append(result)
+
+ var success_count = 0
+ var total_files = files_to_modify.size()
+
+ # Process each file
+ for file_path in files_to_modify.keys():
+ var modifications = files_to_modify[file_path]
+
+ if _modify_file(file_path, modifications, new_name):
+ success_count += 1
+ else:
+ # Only log errors
+ print("Error: Failed to modify %s" % file_path.get_file())
+
+ # Only log if there were failures
+ if success_count != total_files:
+ print("Warning: Only %d/%d files modified successfully" % [success_count, total_files])
+
+ return success_count == total_files
+
+func _modify_file(file_path: String, modifications: Array, new_name: String) -> bool:
+ """Modify all symbol occurrences in a single file"""
+ # Read file content
+ var file = FileAccess.open(file_path, FileAccess.READ)
+ if not file:
+ return false
+
+ var lines = []
+ while not file.eof_reached():
+ lines.append(file.get_line())
+ file.close()
+
+ # Sort by line and column (reverse order for safe replacement)
+ modifications.sort_custom(func(a, b): return a["line_number"] > b["line_number"] or (a["line_number"] == b["line_number"] and a["column"] > b["column"]))
+
+ # Apply all modifications
+ for mod in modifications:
+ var line_idx = mod["line_number"] - 1
+ if line_idx >= 0 and line_idx < lines.size():
+ var old_line = lines[line_idx]
+ var start_pos = mod["match_start"]
+ var end_pos = mod["match_end"]
+
+ # Execute symbol replacement
+ var new_line = old_line.substr(0, start_pos) + new_name + old_line.substr(end_pos)
+ lines[line_idx] = new_line
+
+ # Write back to file
+ file = FileAccess.open(file_path, FileAccess.WRITE)
+ if not file:
+ return false
+
+ for line in lines:
+ file.store_line(line)
+ file.close()
+
+ return true
+
+func _get_current_script_path() -> String:
+ """Get the path of the currently open script"""
+ var script_editor : ScriptEditor = EditorInterface.get_script_editor()
+ if not script_editor:
+ return ""
+
+ # Method 1: Try to get current script directly
+ var current_script = script_editor.get_current_script()
+ if current_script and current_script.resource_path != "":
+ return current_script.resource_path
+
+ # Method 2: Try through current editor
+ var current_editor = script_editor.get_current_editor()
+ if current_editor:
+ if current_editor is ScriptEditorBase:
+ var base_editor = current_editor.get_base_editor()
+ if base_editor and base_editor is CodeEdit:
+ # Try to get script from the editor
+ var script = current_editor.get_edited_resource()
+ if script and script.resource_path != "":
+ return script.resource_path
+
+ # Method 3: Try through editor selection
+ var editor_selection = EditorInterface.get_selection()
+ if editor_selection:
+ var selected = editor_selection.get_selected_nodes()
+ if selected.size() > 0:
+ var node = selected[0]
+ var script = node.get_script()
+ if script and script.resource_path != "":
+ return script.resource_path
+
+ return ""
+
+func _is_script_file(file_path: String) -> bool:
+ """Check if file is a script file we should search in"""
+ var extension = file_path.get_extension().to_lower()
+ return extension in ["gd", "cs", "cpp", "h", "hpp", "c", "py", "js", "ts"]
+
+func _show_error(message: String) -> void:
+ """Show error message"""
+ print("[Rename Symbol] Error: %s" % message)
+ # Could also show a popup or status message
+
+func _validate_symbol_name(name: String) -> bool:
+ """Validate that the symbol name is valid for renaming"""
+ if name.is_empty():
+ return false
+
+ if name == _current_symbol:
+ return false
+
+ # Check for basic identifier rules (letters, numbers, underscore)
+ var regex = RegEx.new()
+ if regex.compile("^[a-zA-Z_][a-zA-Z0-9_]*$") != OK:
+ return true # Fallback to allowing anything if regex fails
+
+ return regex.search(name) != null
+
+func _is_name_compatible(name: String) -> bool:
+ """Check if the name contains only standard ASCII characters"""
+ for i in range(name.length()):
+ var char_code = name.unicode_at(i)
+ # Allow only standard ASCII printable characters (32-126)
+ # Plus common programming characters
+ if char_code < 32 or char_code > 126:
+ return false
+ return true
+
+func _show_success(message: String) -> void:
+ """Show success message"""
+ print("[Rename Symbol] ✅ SUCCESS: %s" % message)
+
+func _refresh_file_system() -> void:
+ """Direct source replacement for immediate synchronization - core functionality"""
+ # Collect all modified file paths (only selected items)
+ var modified_files : PackedStringArray = PackedStringArray()
+ for i in _selected_items:
+ if i >= 0 and i < _rename_results.size():
+ var result = _rename_results[i]
+ if result.has("file_path"):
+ var file_path = result["file_path"]
+ if file_path not in modified_files:
+ modified_files.append(file_path)
+
+ # Limit the number of files to prevent system overload
+ if modified_files.size() > 100:
+ push_warning("[Rename Dialog] Large number of files to refresh: %d" % modified_files.size())
+
+ # Force reload with direct source replacement
+ _force_reload(modified_files)
+
+func _replace_src(path: String, new_text: String) -> void:
+ """Replace source code in open editors - prevents user from losing work state"""
+ var item_list: ItemList = IDE.get_script_list()
+ var editor_container: TabContainer = IDE.get_script_editor_container()
+
+ # Check API availability
+ if not is_instance_valid(item_list) or not is_instance_valid(editor_container):
+ push_warning("[Rename Dialog] IDE components not available for source replacement")
+ return
+
+ if item_list.item_count != editor_container.get_tab_count():
+ push_warning("[Rename Dialog] Script list and editor container count mismatch")
+ return
+
+ # Find and update open file editors
+ for x: int in item_list.item_count:
+ if path == item_list.get_item_tooltip(x):
+ var control: Control = editor_container.get_tab_control(x)
+ if control is ScriptEditorBase:
+ var editor: Control = control.get_base_editor()
+ if editor is CodeEdit:
+ # Save user's current view state
+ var scroll_h: int = editor.scroll_horizontal
+ var scroll_v: int = editor.scroll_vertical
+ var caret_line: int = editor.get_caret_line()
+ var caret_column: int = editor.get_caret_column()
+
+ # Replace source content
+ editor.text = new_text
+
+ # Restore user's view state (scroll position, cursor position)
+ editor.scroll_horizontal = scroll_h
+ editor.scroll_vertical = scroll_v
+ editor.set_caret_line(caret_line)
+ editor.set_caret_column(caret_column)
+ return
+
+func _force_reload(files: PackedStringArray, type_hint: String = "") -> void:
+ """Force reload files, bypassing cache and updating open editors"""
+ for file: String in files:
+ if not ResourceLoader.exists(file):
+ continue
+
+ if ResourceLoader.has_cached(file):
+ # Bypass cache to load fresh content
+ var resource: Resource = ResourceLoader.load(file, type_hint, ResourceLoader.CACHE_MODE_IGNORE)
+ if resource is Script:
+ # Directly replace source in open editors
+ _replace_src(resource.resource_path, resource.source_code)
+
+
+
+
+
+
+func _verify_modifications(new_name: String) -> bool:
+ """Verify that modifications were successfully applied to the file - Quality Assurance Mechanism"""
+ var files_to_check = {}
+ for i in _selected_items:
+ if i >= 0 and i < _rename_results.size():
+ var result = _rename_results[i]
+ var file_path = result["file_path"]
+ if not files_to_check.has(file_path):
+ files_to_check[file_path] = []
+ files_to_check[file_path].append(result)
+
+ var verified_files = 0
+ var total_files = files_to_check.size()
+
+ # Validate modification results file by file
+ for file_path in files_to_check.keys():
+ if _verify_file_modifications(file_path, files_to_check[file_path], new_name):
+ verified_files += 1
+ # Show details only on failures
+ else:
+ print(" ❌ Verification failed: %s" % file_path.get_file())
+
+ var success = verified_files == total_files
+ if not success:
+ print(" ⚠️ Verification: %d/%d files verified" % [verified_files, total_files])
+
+ return success
+
+func _verify_file_modifications(file_path: String, modifications: Array, new_name: String) -> bool:
+ """Verify that modifications in a single file were successful"""
+ var file = FileAccess.open(file_path, FileAccess.READ)
+ if not file:
+ return false
+
+ var lines = []
+ while not file.eof_reached():
+ lines.append(file.get_line())
+ file.close()
+
+ # Check each modification
+ for mod in modifications:
+ var line_idx = mod["line_number"] - 1
+ if line_idx >= 0 and line_idx < lines.size():
+ var line = lines[line_idx]
+ # Check if the new name exists
+ if not new_name in line:
+ return false
+ else:
+ return false
+
+ return true
+
+
+
+func _show_sync_warning() -> void:
+ """Show warning about synchronization issues"""
+ print("[Rename Symbol] ⚠️ Files were modified but verification had issues")
+
+
+func _on_close_requested() -> void:
+ _on_cancel_pressed()
+
+
+func _on_focus_exited() -> void:
+ _on_cancel_pressed()
+
+
+func _setup_dialog_display() -> void:
+ """Setup dialog size and position for proper display"""
+ # Get optimal size for dialog
+ var optimal_size = _get_optimal_dialog_size()
+ size = optimal_size
+
+ # Center the dialog manually
+ _center_dialog()
+
+func _get_optimal_dialog_size() -> Vector2i:
+ """Calculate optimal dialog size based on content and DPI"""
+ # Base size for rename dialog
+ var base_size = Vector2i(800, 600)
+
+ # Get screen info for DPI awareness
+ var screen = DisplayServer.screen_get_size()
+ var dpi_scale = DisplayServer.screen_get_scale()
+
+ # Apply DPI scaling if needed
+ if dpi_scale > 1.0:
+ base_size = Vector2i(
+ int(base_size.x * min(dpi_scale, 1.5)),
+ int(base_size.y * min(dpi_scale, 1.5))
+ )
+
+ # Ensure minimum size for rename dialog with code preview
+ base_size.x = max(base_size.x, 700)
+ base_size.y = max(base_size.y, 500)
+
+ # Ensure it fits on screen (leave 100px margin)
+ base_size.x = min(base_size.x, screen.x - 100)
+ base_size.y = min(base_size.y, screen.y - 100)
+
+ return base_size
+
+func _center_dialog() -> void:
+ """Manually center the dialog on screen"""
+ var screen_size = DisplayServer.screen_get_size()
+ var dialog_size = size
+
+ # Calculate centered position
+ var centered_pos = Vector2i(
+ (screen_size.x - dialog_size.x) / 2,
+ (screen_size.y - dialog_size.y) / 2
+ )
+
+ # Ensure dialog stays on screen
+ centered_pos.x = max(0, centered_pos.x)
+ centered_pos.y = max(0, centered_pos.y)
+
+ # Set position
+ position = centered_pos
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd.uid b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd.uid
new file mode 100644
index 0000000..802fd41
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd.uid
@@ -0,0 +1 @@
+uid://b26eh3hs86gi4
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.tscn b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.tscn
new file mode 100644
index 0000000..ab5c30a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.tscn
@@ -0,0 +1,107 @@
+[gd_scene load_steps=2 format=3 uid="uid://byndwttsl5vau"]
+
+[ext_resource type="Script" uid="uid://b26eh3hs86gi4" path="res://addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.gd" id="1_rqbkt"]
+
+[node name="Window" type="Window" node_paths=PackedStringArray("new_name_edit", "scope_option", "preview_tree", "rename_button", "cancel_button", "select_all_button", "unselect_all_button", "selection_status")]
+title = "Rename Symbol"
+position = Vector2i(0, 36)
+size = Vector2i(800, 600)
+exclusive = true
+script = ExtResource("1_rqbkt")
+new_name_edit = NodePath("MarginContainer/MainContainer/HeaderContainer/NewNameEdit")
+scope_option = NodePath("MarginContainer/MainContainer/HeaderContainer/ScopeOption")
+preview_tree = NodePath("MarginContainer/MainContainer/PreviewContainer/PreviewTree")
+rename_button = NodePath("MarginContainer/MainContainer/HBoxContainer/RenameButton")
+cancel_button = NodePath("MarginContainer/MainContainer/HBoxContainer/CancelButton")
+select_all_button = NodePath("MarginContainer/MainContainer/SelectionControls/SelectAllButton")
+unselect_all_button = NodePath("MarginContainer/MainContainer/SelectionControls/UnselectAllButton")
+selection_status = NodePath("MarginContainer/MainContainer/SelectionControls/SelectionStatus")
+
+[node name="MarginContainer" type="MarginContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_constants/margin_left = 10
+theme_override_constants/margin_top = 10
+theme_override_constants/margin_right = 10
+theme_override_constants/margin_bottom = 10
+
+[node name="MainContainer" type="VBoxContainer" parent="MarginContainer"]
+layout_mode = 2
+theme_override_constants/separation = 10
+
+[node name="HeaderContainer" type="VBoxContainer" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+size_flags_vertical = 4
+theme_override_constants/separation = 5
+
+[node name="SymbolLabel" type="Label" parent="MarginContainer/MainContainer/HeaderContainer"]
+layout_mode = 2
+text = "Rename symbol to:"
+autowrap_mode = 3
+
+[node name="NewNameEdit" type="LineEdit" parent="MarginContainer/MainContainer/HeaderContainer"]
+layout_mode = 2
+placeholder_text = "Enter new name..."
+select_all_on_focus = true
+
+[node name="ScopeOption" type="OptionButton" parent="MarginContainer/MainContainer/HeaderContainer"]
+layout_mode = 2
+selected = 1
+item_count = 2
+popup/item_0/text = "Current File Only"
+popup/item_0/id = 0
+popup/item_1/text = "Entire Project"
+popup/item_1/id = 1
+
+[node name="SelectionControls" type="HBoxContainer" parent="MarginContainer/MainContainer"]
+layout_direction = 2
+layout_mode = 2
+size_flags_vertical = 4
+theme_override_constants/separation = 10
+
+[node name="SelectionStatus" type="Label" parent="MarginContainer/MainContainer/SelectionControls"]
+layout_direction = 2
+layout_mode = 2
+text = "0 of 0 selected"
+horizontal_alignment = 2
+
+[node name="SelectAllButton" type="Button" parent="MarginContainer/MainContainer/SelectionControls"]
+layout_mode = 2
+text = "Select All"
+alignment = 2
+
+[node name="UnselectAllButton" type="Button" parent="MarginContainer/MainContainer/SelectionControls"]
+layout_mode = 2
+text = "Unselect All"
+alignment = 2
+
+[node name="PreviewContainer" type="HSplitContainer" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="PreviewTree" type="Tree" parent="MarginContainer/MainContainer/PreviewContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+columns = 3
+column_titles_visible = true
+hide_root = true
+
+[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MainContainer"]
+layout_mode = 2
+size_flags_vertical = 4
+theme_override_constants/separation = 10
+alignment = 2
+
+[node name="RenameButton" type="Button" parent="MarginContainer/MainContainer/HBoxContainer"]
+layout_mode = 2
+text = "Rename"
+
+[node name="CancelButton" type="Button" parent="MarginContainer/MainContainer/HBoxContainer"]
+layout_mode = 2
+text = "Cancel"
+
+[connection signal="close_requested" from="." to="." method="_on_close_requested"]
+[connection signal="focus_exited" from="." to="." method="_on_focus_exited"]
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.cfg b/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.cfg
new file mode 100644
index 0000000..825ea0d
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="Symbol Navigator"
+description="Find references and rename symbols across the project"
+author="kyros"
+version="3.0"
+script="plugin.gd"
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd b/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd
new file mode 100644
index 0000000..cb9b88a
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd
@@ -0,0 +1,203 @@
+@tool
+extends EditorPlugin
+# =============================================================================
+# Author: GodotIDE Team
+# Symbol Navigator
+#
+# Find references and rename symbols across the project
+# =============================================================================
+
+const FIND_REFERENCES_PANEL_UI = preload("res://addons/_Godot-IDE_/plugins/symbol_navigator/gui/find_references_panel.tscn")
+const RENAME_SYMBOL_UI = preload("res://addons/_Godot-IDE_/plugins/symbol_navigator/gui/rename_dialog.tscn")
+
+var _find_references_panel : Control = null
+var _find_references_panel_button : Button = null
+var _rename_dialog : Window = null
+
+# Input shortcuts
+var _find_references_input : InputEventKey = null
+var _rename_symbol_input : InputEventKey = null
+
+# Menu items
+var _context_menu_plugin : EditorPlugin = null
+
+func _init() -> void:
+ # Initialize shortcuts
+ _setup_shortcuts()
+
+func _setup_shortcuts() -> void:
+ # Find references shortcut (Shift+F12)
+ var find_input : Variant = IDE.get_config("symbol_navigator", "find_references_input")
+ if find_input is InputEventKey:
+ _find_references_input = find_input
+ else:
+ _find_references_input = InputEventKey.new()
+ _find_references_input.pressed = true
+ _find_references_input.shift_pressed = true
+ _find_references_input.keycode = KEY_F12
+ IDE.set_config("symbol_navigator", "find_references_input", _find_references_input)
+
+ # Rename symbol shortcut (F2)
+ var rename_input : Variant = IDE.get_config("symbol_navigator", "rename_symbol_input")
+ if rename_input is InputEventKey:
+ _rename_symbol_input = rename_input
+ else:
+ _rename_symbol_input = InputEventKey.new()
+ _rename_symbol_input.pressed = true
+ _rename_symbol_input.keycode = KEY_F2
+ IDE.set_config("symbol_navigator", "rename_symbol_input", _rename_symbol_input)
+
+func _enter_tree() -> void:
+ # Plugin is activated
+ if IDE.debug:
+ print("[Symbol Navigator] Plugin activated")
+
+ # Set up bottom panel for find references
+ _setup_bottom_panel()
+
+func _exit_tree() -> void:
+ # Clean up bottom panel
+ if is_instance_valid(_find_references_panel):
+ remove_control_from_bottom_panel(_find_references_panel)
+ _find_references_panel.queue_free()
+ _find_references_panel = null
+ _find_references_panel_button = null
+
+ # Clean up rename dialog
+ if is_instance_valid(_rename_dialog):
+ _rename_dialog.queue_free()
+ _rename_dialog = null
+
+ if IDE.debug:
+ print("[Symbol Navigator] Plugin deactivated")
+
+func _input(event: InputEvent) -> void:
+ if not event.is_pressed():
+ return
+
+ # Handle find references shortcut
+ if event.is_match(_find_references_input):
+ _open_find_references()
+
+ # Handle rename symbol shortcut
+ elif event.is_match(_rename_symbol_input):
+ _open_rename_dialog()
+
+func _open_find_references() -> void:
+ var current_symbol = _get_symbol_at_cursor()
+ if current_symbol.is_empty():
+ push_warning("[Symbol Navigator] No symbol found at cursor")
+ return
+
+ # Ensure the bottom panel is set up
+ if not is_instance_valid(_find_references_panel):
+ _setup_bottom_panel()
+
+ if is_instance_valid(_find_references_panel):
+ # Make the bottom panel visible first
+ make_bottom_panel_item_visible(_find_references_panel)
+
+ # Try to call search_symbol with error handling
+ if _find_references_panel.has_method("search_symbol"):
+ _find_references_panel.search_symbol(current_symbol)
+ if _find_references_panel.has_method("show_and_focus"):
+ _find_references_panel.show_and_focus()
+ else:
+ push_warning("[Symbol Navigator] Panel search_symbol method not available, using fallback...")
+ # Just show the panel even if we can't search
+ if _find_references_panel.has_method("show_and_focus"):
+ _find_references_panel.show_and_focus()
+ else:
+ push_error("[Symbol Navigator] Failed to create or access find references panel")
+
+func _open_rename_dialog() -> void:
+ var current_symbol = _get_symbol_at_cursor()
+ if current_symbol.is_empty():
+ push_warning("[Symbol Navigator] No symbol found at cursor")
+ return
+
+ print("[Symbol Navigator] Opening rename dialog for symbol: ", current_symbol)
+
+ if not is_instance_valid(_rename_dialog):
+ print("[Symbol Navigator] Creating new rename dialog instance")
+ _rename_dialog = RENAME_SYMBOL_UI.instantiate()
+ add_child(_rename_dialog)
+
+ # Wait for the dialog to be ready before calling methods on it
+ if not _rename_dialog.is_node_ready():
+ print("[Symbol Navigator] Dialog not ready, waiting for ready signal")
+ await _rename_dialog.ready
+
+ print("[Symbol Navigator] Dialog ready, setting symbol")
+
+ _rename_dialog.set_symbol(current_symbol)
+ _rename_dialog.popup_centered()
+
+# Get symbol at current cursor position
+func _get_symbol_at_cursor() -> String:
+ var script_editor : ScriptEditor = EditorInterface.get_script_editor()
+ if not script_editor:
+ return ""
+
+ var current_editor = script_editor.get_current_editor()
+ if not current_editor:
+ return ""
+
+ var code_edit : CodeEdit = current_editor.get_base_editor()
+ if not code_edit:
+ return ""
+
+ var caret_line = code_edit.get_caret_line()
+ var caret_column = code_edit.get_caret_column()
+ var line_text = code_edit.get_line(caret_line)
+
+ # Extract word at cursor position
+ return _extract_word_at_position(line_text, caret_column)
+
+# Extract word at specific column position in text
+func _extract_word_at_position(text: String, column: int) -> String:
+ if text.is_empty() or column < 0 or column >= text.length():
+ return ""
+
+ # Word boundary characters
+ var word_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
+
+ # Find start of word
+ var start = column
+ while start > 0 and word_chars.contains(text[start - 1]):
+ start -= 1
+
+ # Find end of word
+ var end = column
+ while end < text.length() and word_chars.contains(text[end]):
+ end += 1
+
+ if start == end:
+ return ""
+
+ return text.substr(start, end - start)
+
+func _setup_bottom_panel() -> void:
+ """Set up the find references bottom panel"""
+ if is_instance_valid(_find_references_panel):
+ return # Already set up
+
+ # Create the panel instance
+ _find_references_panel = FIND_REFERENCES_PANEL_UI.instantiate()
+
+ if not _find_references_panel:
+ push_error("[Symbol Navigator] Failed to instantiate find references panel")
+ return
+
+ # Simple validation - just check if it's a Control
+ if not _find_references_panel is Control:
+ push_error("[Symbol Navigator] Panel is not a Control node")
+ _find_references_panel.queue_free()
+ _find_references_panel = null
+ return
+
+ # Add it to the bottom panel with a tab
+ _find_references_panel_button = add_control_to_bottom_panel(_find_references_panel, "Find References")
+
+ if IDE.debug:
+ print("[Symbol Navigator] Bottom panel 'Find References' added successfully")
diff --git a/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd.uid b/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd.uid
new file mode 100644
index 0000000..2652d29
--- /dev/null
+++ b/addons/_Godot-IDE_/plugins/symbol_navigator/plugin.gd.uid
@@ -0,0 +1 @@
+uid://hckp1nfwdm5l
diff --git a/addons/_Godot-IDE_/shared_resources/CREDITS.txt b/addons/_Godot-IDE_/shared_resources/CREDITS.txt
new file mode 100644
index 0000000..ce04a3b
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/CREDITS.txt
@@ -0,0 +1,10 @@
+CREDITS
+
+Icons may vary from those:
+ * Downloaded from godot repository.
+ * Created by CodeNameTwister.
+ * Created by Community.
+
+URL:
+ github: https://github.com/godotengine/godot
+ github: https://github.com/CodeNameTwister
diff --git a/addons/_Godot-IDE_/shared_resources/InterfaceScript.svg b/addons/_Godot-IDE_/shared_resources/InterfaceScript.svg
new file mode 100644
index 0000000..0ddcb11
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/InterfaceScript.svg
@@ -0,0 +1,72 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/InterfaceScript.svg.import b/addons/_Godot-IDE_/shared_resources/InterfaceScript.svg.import
new file mode 100644
index 0000000..47de66b
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/InterfaceScript.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bjmtfc58y1sbs"
+path="res://.godot/imported/InterfaceScript.svg-978d78f5291d69d37e8f5c5fbdda303f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/InterfaceScript.svg"
+dest_files=["res://.godot/imported/InterfaceScript.svg-978d78f5291d69d37e8f5c5fbdda303f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg b/addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg
new file mode 100644
index 0000000..b2b486e
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg.import b/addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg.import
new file mode 100644
index 0000000..0243c23
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dl6qns43xyjnv"
+path="res://.godot/imported/MemberAnnotation.svg-66dc87598be9e506c35ad9c58ce00471.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberAnnotation.svg"
+dest_files=["res://.godot/imported/MemberAnnotation.svg-66dc87598be9e506c35ad9c58ce00471.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberConstant.svg b/addons/_Godot-IDE_/shared_resources/MemberConstant.svg
new file mode 100644
index 0000000..d4622ce
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberConstant.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberConstant.svg.import b/addons/_Godot-IDE_/shared_resources/MemberConstant.svg.import
new file mode 100644
index 0000000..327285c
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberConstant.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ouml4wypk5bq"
+path="res://.godot/imported/MemberConstant.svg-4dc55bf9072736db9c3b22d3a1c92bb2.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberConstant.svg"
+dest_files=["res://.godot/imported/MemberConstant.svg-4dc55bf9072736db9c3b22d3a1c92bb2.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberConstructor.svg b/addons/_Godot-IDE_/shared_resources/MemberConstructor.svg
new file mode 100644
index 0000000..0298086
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberConstructor.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberConstructor.svg.import b/addons/_Godot-IDE_/shared_resources/MemberConstructor.svg.import
new file mode 100644
index 0000000..af27211
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberConstructor.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d4jeah6wyiv8r"
+path="res://.godot/imported/MemberConstructor.svg-ed9f2c87bf749cf455de04099172a17e.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberConstructor.svg"
+dest_files=["res://.godot/imported/MemberConstructor.svg-ed9f2c87bf749cf455de04099172a17e.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberMethod.svg b/addons/_Godot-IDE_/shared_resources/MemberMethod.svg
new file mode 100644
index 0000000..9183525
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberMethod.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberMethod.svg.import b/addons/_Godot-IDE_/shared_resources/MemberMethod.svg.import
new file mode 100644
index 0000000..a3b0f01
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberMethod.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cgdceyfng83st"
+path="res://.godot/imported/MemberMethod.svg-e10674ee5ab6973a2b54597a1c256593.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberMethod.svg"
+dest_files=["res://.godot/imported/MemberMethod.svg-e10674ee5ab6973a2b54597a1c256593.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberOperator.svg b/addons/_Godot-IDE_/shared_resources/MemberOperator.svg
new file mode 100644
index 0000000..1de44e1
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberOperator.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberOperator.svg.import b/addons/_Godot-IDE_/shared_resources/MemberOperator.svg.import
new file mode 100644
index 0000000..4e5ef4f
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberOperator.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://qjgp3e2bfkdt"
+path="res://.godot/imported/MemberOperator.svg-c5b91d60b0472f74f035d73b4040fe2b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberOperator.svg"
+dest_files=["res://.godot/imported/MemberOperator.svg-c5b91d60b0472f74f035d73b4040fe2b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberProperty.svg b/addons/_Godot-IDE_/shared_resources/MemberProperty.svg
new file mode 100644
index 0000000..52c1ce6
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberProperty.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberProperty.svg.import b/addons/_Godot-IDE_/shared_resources/MemberProperty.svg.import
new file mode 100644
index 0000000..cc9196b
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberProperty.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cec6jsyssk4ys"
+path="res://.godot/imported/MemberProperty.svg-1ddceeee17fe7a22098a69e182e66fca.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberProperty.svg"
+dest_files=["res://.godot/imported/MemberProperty.svg-1ddceeee17fe7a22098a69e182e66fca.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MemberSignal.svg b/addons/_Godot-IDE_/shared_resources/MemberSignal.svg
new file mode 100644
index 0000000..f36ffa1
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberSignal.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MemberSignal.svg.import b/addons/_Godot-IDE_/shared_resources/MemberSignal.svg.import
new file mode 100644
index 0000000..31e7077
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MemberSignal.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bu2y52as0gijo"
+path="res://.godot/imported/MemberSignal.svg-8254fad67fe0c20a2c2884abbd96d40f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MemberSignal.svg"
+dest_files=["res://.godot/imported/MemberSignal.svg-8254fad67fe0c20a2c2884abbd96d40f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MethodOverride.svg b/addons/_Godot-IDE_/shared_resources/MethodOverride.svg
new file mode 100644
index 0000000..6210f0e
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MethodOverride.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MethodOverride.svg.import b/addons/_Godot-IDE_/shared_resources/MethodOverride.svg.import
new file mode 100644
index 0000000..329b093
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MethodOverride.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cjpyaeqxjujsy"
+path="res://.godot/imported/MethodOverride.svg-77874f3b27ae11b68bc31d64d64b0ff7.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MethodOverride.svg"
+dest_files=["res://.godot/imported/MethodOverride.svg-77874f3b27ae11b68bc31d64d64b0ff7.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/MiniObject.svg b/addons/_Godot-IDE_/shared_resources/MiniObject.svg
new file mode 100644
index 0000000..359c132
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MiniObject.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/MiniObject.svg.import b/addons/_Godot-IDE_/shared_resources/MiniObject.svg.import
new file mode 100644
index 0000000..0a54183
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/MiniObject.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://qlqlwh2m5g2g"
+path="res://.godot/imported/MiniObject.svg-89475cde474fae096b341c01ac17ea07.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/MiniObject.svg"
+dest_files=["res://.godot/imported/MiniObject.svg-89475cde474fae096b341c01ac17ea07.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/PluginScript.svg b/addons/_Godot-IDE_/shared_resources/PluginScript.svg
new file mode 100644
index 0000000..910efc3
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/PluginScript.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/PluginScript.svg.import b/addons/_Godot-IDE_/shared_resources/PluginScript.svg.import
new file mode 100644
index 0000000..bc80fa7
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/PluginScript.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://byubjxffpi1m0"
+path="res://.godot/imported/PluginScript.svg-d9d886078c7444d411aced8d4bd81c3c.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/PluginScript.svg"
+dest_files=["res://.godot/imported/PluginScript.svg-d9d886078c7444d411aced8d4bd81c3c.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/Script.svg b/addons/_Godot-IDE_/shared_resources/Script.svg
new file mode 100644
index 0000000..4e418ab
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/Script.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/Script.svg.import b/addons/_Godot-IDE_/shared_resources/Script.svg.import
new file mode 100644
index 0000000..5b669d6
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/Script.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://mv5pf1qwmqho"
+path="res://.godot/imported/Script.svg-d05847253586d81271dfb434935b6178.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/Script.svg"
+dest_files=["res://.godot/imported/Script.svg-d05847253586d81271dfb434935b6178.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/ScriptExtend.svg b/addons/_Godot-IDE_/shared_resources/ScriptExtend.svg
new file mode 100644
index 0000000..0b57798
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/ScriptExtend.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/ScriptExtend.svg.import b/addons/_Godot-IDE_/shared_resources/ScriptExtend.svg.import
new file mode 100644
index 0000000..2ba6e41
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/ScriptExtend.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dqfn3tghaer0y"
+path="res://.godot/imported/ScriptExtend.svg-07de63a340541258754fef7b071c3168.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/ScriptExtend.svg"
+dest_files=["res://.godot/imported/ScriptExtend.svg-07de63a340541258754fef7b071c3168.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/Tools.svg b/addons/_Godot-IDE_/shared_resources/Tools.svg
new file mode 100644
index 0000000..2ba2fc4
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/Tools.svg
@@ -0,0 +1 @@
+
diff --git a/addons/_Godot-IDE_/shared_resources/Tools.svg.import b/addons/_Godot-IDE_/shared_resources/Tools.svg.import
new file mode 100644
index 0000000..58b1238
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/Tools.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bayjudew7j16p"
+path="res://.godot/imported/Tools.svg-019ff87dc535ddd3db11c2c200d22113.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/Tools.svg"
+dest_files=["res://.godot/imported/Tools.svg-019ff87dc535ddd3db11c2c200d22113.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/check.svg b/addons/_Godot-IDE_/shared_resources/check.svg
new file mode 100644
index 0000000..921112b
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/check.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/check.svg.import b/addons/_Godot-IDE_/shared_resources/check.svg.import
new file mode 100644
index 0000000..ff84953
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/check.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://csrtms7lvdx8w"
+path="res://.godot/imported/check.svg-b87e99db7758119a786b0c553010930b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/check.svg"
+dest_files=["res://.godot/imported/check.svg-b87e99db7758119a786b0c553010930b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/dot.svg b/addons/_Godot-IDE_/shared_resources/dot.svg
new file mode 100644
index 0000000..1d5927f
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/dot.svg
@@ -0,0 +1,41 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/dot.svg.import b/addons/_Godot-IDE_/shared_resources/dot.svg.import
new file mode 100644
index 0000000..6533349
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/dot.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://lp3f3g1amkc7"
+path="res://.godot/imported/dot.svg-63df02addb58f3150f7ea8b487e79315.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/dot.svg"
+dest_files=["res://.godot/imported/dot.svg-63df02addb58f3150f7ea8b487e79315.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/down.svg b/addons/_Godot-IDE_/shared_resources/down.svg
new file mode 100644
index 0000000..cbe103f
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/down.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/down.svg.import b/addons/_Godot-IDE_/shared_resources/down.svg.import
new file mode 100644
index 0000000..17500c9
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/down.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c2oodbi52ec3m"
+path="res://.godot/imported/down.svg-84b9ebb55f51b2dc7e4d105afecfa4c0.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/down.svg"
+dest_files=["res://.godot/imported/down.svg-84b9ebb55f51b2dc7e4d105afecfa4c0.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/func_private.svg b/addons/_Godot-IDE_/shared_resources/func_private.svg
new file mode 100644
index 0000000..9957f77
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/func_private.svg
@@ -0,0 +1,47 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/func_private.svg.import b/addons/_Godot-IDE_/shared_resources/func_private.svg.import
new file mode 100644
index 0000000..bf9761c
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/func_private.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://do4gmovks0mn6"
+path="res://.godot/imported/func_private.svg-95314f7c2fb9613d1b172db455694da5.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/func_private.svg"
+dest_files=["res://.godot/imported/func_private.svg-95314f7c2fb9613d1b172db455694da5.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/func_public.svg b/addons/_Godot-IDE_/shared_resources/func_public.svg
new file mode 100644
index 0000000..7d40eec
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/func_public.svg
@@ -0,0 +1,55 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/func_public.svg.import b/addons/_Godot-IDE_/shared_resources/func_public.svg.import
new file mode 100644
index 0000000..2084100
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/func_public.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ckc3yk6f8y3ob"
+path="res://.godot/imported/func_public.svg-e84d2a0b8a76de4cc140c8ac1fc5fa19.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/func_public.svg"
+dest_files=["res://.godot/imported/func_public.svg-e84d2a0b8a76de4cc140c8ac1fc5fa19.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/func_virtual.svg b/addons/_Godot-IDE_/shared_resources/func_virtual.svg
new file mode 100644
index 0000000..cfedb37
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/func_virtual.svg
@@ -0,0 +1,40 @@
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/func_virtual.svg.import b/addons/_Godot-IDE_/shared_resources/func_virtual.svg.import
new file mode 100644
index 0000000..6573de7
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/func_virtual.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dg7rdmg80x4jv"
+path="res://.godot/imported/func_virtual.svg-80710816056a85c8137e952bcaa38480.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/func_virtual.svg"
+dest_files=["res://.godot/imported/func_virtual.svg-80710816056a85c8137e952bcaa38480.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/search.svg b/addons/_Godot-IDE_/shared_resources/search.svg
new file mode 100644
index 0000000..b4582cc
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/search.svg
@@ -0,0 +1,55 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/search.svg.import b/addons/_Godot-IDE_/shared_resources/search.svg.import
new file mode 100644
index 0000000..c3ab77b
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/search.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://816fejewtbfj"
+path="res://.godot/imported/search.svg-eb5038c2ffb72bd3926a7614da05a411.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/search.svg"
+dest_files=["res://.godot/imported/search.svg-eb5038c2ffb72bd3926a7614da05a411.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/static.svg b/addons/_Godot-IDE_/shared_resources/static.svg
new file mode 100644
index 0000000..af9ae2d
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/static.svg
@@ -0,0 +1,58 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/static.svg.import b/addons/_Godot-IDE_/shared_resources/static.svg.import
new file mode 100644
index 0000000..225fdbc
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/static.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ds6tdwn7834jk"
+path="res://.godot/imported/static.svg-2d7e8777fe546366b9cbd2e7140c27b2.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/static.svg"
+dest_files=["res://.godot/imported/static.svg-2d7e8777fe546366b9cbd2e7140c27b2.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/up.svg b/addons/_Godot-IDE_/shared_resources/up.svg
new file mode 100644
index 0000000..939c201
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/up.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/addons/_Godot-IDE_/shared_resources/up.svg.import b/addons/_Godot-IDE_/shared_resources/up.svg.import
new file mode 100644
index 0000000..5a4131c
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/up.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bgjo43cuidob1"
+path="res://.godot/imported/up.svg-5eba3501c749f279a18720dc3ddb6226.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/up.svg"
+dest_files=["res://.godot/imported/up.svg-5eba3501c749f279a18720dc3ddb6226.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/_Godot-IDE_/shared_resources/warning.png b/addons/_Godot-IDE_/shared_resources/warning.png
new file mode 100644
index 0000000..3627ca0
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/warning.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44906c3c38edec5fd2281617b09b07c9121e72f0a01b6f663d1b9bd1c6d3f734
+size 238
diff --git a/addons/_Godot-IDE_/shared_resources/warning.png.import b/addons/_Godot-IDE_/shared_resources/warning.png.import
new file mode 100644
index 0000000..21e586c
--- /dev/null
+++ b/addons/_Godot-IDE_/shared_resources/warning.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://x8xl24etv1nb"
+path="res://.godot/imported/warning.png-7a2d105cf05eee357eaf1264447091e5.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/_Godot-IDE_/shared_resources/warning.png"
+dest_files=["res://.godot/imported/warning.png-7a2d105cf05eee357eaf1264447091e5.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/project.godot b/project.godot
index 1ef97d2..0413754 100644
--- a/project.godot
+++ b/project.godot
@@ -8,11 +8,15 @@
config_version=5
+[animation]
+
+compatibility/default_parent_skeleton_in_mesh_instance_3d=true
+
[application]
config/name="TheFlipSide"
run/main_scene="res://Scenes/main.tscn"
-config/features=PackedStringArray("4.5", "Forward Plus")
+config/features=PackedStringArray("4.6", "Forward Plus")
[autoload]
@@ -22,6 +26,17 @@ al_globals="*res://Scripts/al_globals.gd"
project/assembly_name="TheFlipSide"
+[editor_plugins]
+
+enabled=PackedStringArray("res://addons/_Godot-IDE_/plugin.cfg")
+
+[file_customization]
+
+folder_colors={
+"res://addons/_Godot-IDE_/plugins": "blue",
+"res://addons/_Godot-IDE_/shared_resources/": "teal"
+}
+
[input]
flip={