first Prototype
This commit is contained in:
parent
e11825c698
commit
35ce267482
481 changed files with 17315 additions and 1 deletions
336
addons/script-ide/override/override_panel.gd
Normal file
336
addons/script-ide/override/override_panel.gd
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
@tool
|
||||
extends PopupPanel
|
||||
|
||||
const FUNC_META: StringName = &"func"
|
||||
|
||||
@onready var filter_txt: LineEdit = %FilterTxt
|
||||
@onready var class_func_tree: Tree = %ClassFuncTree
|
||||
@onready var ok_btn: Button = %OkBtn
|
||||
@onready var cancel_btn: Button = %CancelBtn
|
||||
|
||||
var plugin: EditorPlugin
|
||||
|
||||
var selections: Dictionary[String, bool] = {} # Used as Set.
|
||||
var class_to_functions: Dictionary[StringName, PackedStringArray]
|
||||
|
||||
func _ready() -> void:
|
||||
filter_txt.text_changed.connect(update_tree_filter.unbind(1))
|
||||
|
||||
class_func_tree.multi_selected.connect(func(item: TreeItem, col: int, selected: bool): save_selection(selected, item))
|
||||
class_func_tree.item_activated.connect(generate_functions)
|
||||
|
||||
cancel_btn.pressed.connect(hide)
|
||||
ok_btn.pressed.connect(generate_functions)
|
||||
|
||||
about_to_popup.connect(on_show)
|
||||
|
||||
if (plugin != null):
|
||||
filter_txt.gui_input.connect(navigate_on_tree)
|
||||
|
||||
func navigate_on_tree(event: InputEvent):
|
||||
if (event.is_action_pressed(&"ui_down", true)):
|
||||
var selected: TreeItem = get_selected_tree_item()
|
||||
if (selected == null):
|
||||
return
|
||||
var item: TreeItem = selected.get_next_in_tree()
|
||||
if (item == null):
|
||||
return
|
||||
|
||||
focus_tree_item(item)
|
||||
elif (event.is_action_pressed(&"ui_up", true)):
|
||||
var selected: TreeItem = get_selected_tree_item()
|
||||
if (selected == null):
|
||||
return
|
||||
var item: TreeItem = selected.get_prev_in_tree()
|
||||
if (item == null):
|
||||
return
|
||||
|
||||
focus_tree_item(item)
|
||||
elif (event.is_action_pressed(&"ui_page_down", true)):
|
||||
var selected: TreeItem = get_selected_tree_item()
|
||||
if (selected == null):
|
||||
return
|
||||
|
||||
var item: TreeItem = selected.get_next_in_tree()
|
||||
if (item == null):
|
||||
return
|
||||
|
||||
for index: int in 4:
|
||||
var next: TreeItem = item.get_next_in_tree()
|
||||
if (next == null):
|
||||
break
|
||||
item = next
|
||||
|
||||
focus_tree_item(item)
|
||||
elif (event.is_action_pressed(&"ui_page_up", true)):
|
||||
var selected: TreeItem = get_selected_tree_item()
|
||||
if (selected == null):
|
||||
return
|
||||
|
||||
var item: TreeItem = selected.get_prev_in_tree()
|
||||
if (item == null):
|
||||
return
|
||||
|
||||
for index: int in 4:
|
||||
var prev: TreeItem = item.get_prev_in_tree()
|
||||
if (prev == null):
|
||||
break
|
||||
item = prev
|
||||
|
||||
focus_tree_item(item)
|
||||
elif (event.is_action_pressed(&"ui_select", true)):
|
||||
var selected: TreeItem = get_selected_tree_item()
|
||||
if (selected == null):
|
||||
return
|
||||
|
||||
if (!selected.is_selectable(0)):
|
||||
selected.collapsed = !selected.collapsed
|
||||
class_func_tree.accept_event()
|
||||
return
|
||||
|
||||
if (selected.is_selected(0)):
|
||||
selected.deselect(0)
|
||||
save_selection(false, selected)
|
||||
else:
|
||||
selected.select(0)
|
||||
save_selection(true, selected)
|
||||
|
||||
class_func_tree.accept_event()
|
||||
elif (event.is_action_pressed(&"ui_text_submit", true)):
|
||||
if (selections.size() == 0):
|
||||
return
|
||||
|
||||
generate_functions()
|
||||
class_func_tree.accept_event()
|
||||
|
||||
func get_selected_tree_item() -> TreeItem:
|
||||
var selected: TreeItem = class_func_tree.get_selected()
|
||||
if (selected == null):
|
||||
selected = class_func_tree.get_root()
|
||||
return selected
|
||||
|
||||
func focus_tree_item(item: TreeItem):
|
||||
var was_selected: bool = item.is_selected(0)
|
||||
item.select(0)
|
||||
item.deselect(0)
|
||||
if (was_selected):
|
||||
item.select(0)
|
||||
|
||||
class_func_tree.ensure_cursor_is_visible()
|
||||
class_func_tree.accept_event()
|
||||
|
||||
func update_tree_filter():
|
||||
update_tree()
|
||||
|
||||
func save_selection(selected: bool, item: TreeItem):
|
||||
if (selected):
|
||||
selections[item.get_text(0)] = true
|
||||
else:
|
||||
selections.erase(item.get_text(0))
|
||||
|
||||
ok_btn.disabled = selections.size() == 0
|
||||
|
||||
func on_show():
|
||||
class_func_tree.clear()
|
||||
selections.clear()
|
||||
ok_btn.disabled = true
|
||||
filter_txt.text = &""
|
||||
|
||||
var script: Script = EditorInterface.get_script_editor().get_current_script()
|
||||
class_to_functions = collect_all_class_functions(script) # [StringName, PackedStringArray]
|
||||
if (class_to_functions.is_empty()):
|
||||
return
|
||||
|
||||
update_tree()
|
||||
filter_txt.grab_focus()
|
||||
|
||||
func update_tree():
|
||||
class_func_tree.clear()
|
||||
|
||||
var text: String = filter_txt.text
|
||||
|
||||
var root: TreeItem = class_func_tree.create_item()
|
||||
for class_name_str: StringName in class_to_functions.keys():
|
||||
var class_item: TreeItem = root.create_child(0)
|
||||
class_item.set_selectable(0, false)
|
||||
class_item.set_text(0, class_name_str)
|
||||
|
||||
for function: String in class_to_functions.get(class_name_str):
|
||||
var is_preselected: bool = selections.has(function)
|
||||
if (is_preselected || text.is_empty() || text.is_subsequence_ofn(function)):
|
||||
var func_item: TreeItem = class_item.create_child()
|
||||
func_item.set_text(0, function)
|
||||
if (plugin.keywords.has(function.get_slice("(", 0))):
|
||||
func_item.set_icon(0, plugin.engine_func_icon)
|
||||
else:
|
||||
func_item.set_icon(0, plugin.func_icon)
|
||||
|
||||
if (is_preselected):
|
||||
func_item.select(0)
|
||||
|
||||
func collect_all_class_functions(script: Script) -> Dictionary[StringName, PackedStringArray]:
|
||||
var existing_funcs: Dictionary[String, bool] = {} # Used as Set.
|
||||
for func_str: String in plugin.outline_cache.engine_funcs:
|
||||
existing_funcs[func_str] = true
|
||||
for func_str: String in plugin.outline_cache.funcs:
|
||||
existing_funcs[func_str] = true
|
||||
|
||||
var class_to_functions: Dictionary[StringName, PackedStringArray] = collect_super_class_functions(script.get_base_script(), existing_funcs)
|
||||
var native_class_to_functions: Dictionary[StringName, PackedStringArray] = collect_native_class_functions(script.get_instance_base_type(), existing_funcs)
|
||||
|
||||
return native_class_to_functions.merged(class_to_functions)
|
||||
|
||||
func collect_super_class_functions(base_script: Script, existing_funcs: Dictionary[String, bool]) -> Dictionary[StringName, PackedStringArray]:
|
||||
var super_classes: Array[Script] = []
|
||||
while (base_script != null):
|
||||
super_classes.insert(0, base_script)
|
||||
|
||||
base_script = base_script.get_base_script()
|
||||
|
||||
var class_to_functions: Dictionary[StringName, PackedStringArray] = {}
|
||||
for super_class: Script in super_classes:
|
||||
var functions: PackedStringArray = collect_script_functions(super_class, existing_funcs)
|
||||
if (functions.is_empty()):
|
||||
continue
|
||||
|
||||
class_to_functions[super_class.get_global_name()] = functions
|
||||
|
||||
return class_to_functions
|
||||
|
||||
func collect_native_class_functions(native_class: StringName, existing_funcs: Dictionary[String, bool]) -> Dictionary[StringName, PackedStringArray]:
|
||||
var super_native_classes: Array[StringName] = []
|
||||
while (native_class != &""):
|
||||
super_native_classes.insert(0, native_class)
|
||||
|
||||
native_class = ClassDB.get_parent_class(native_class)
|
||||
|
||||
var class_to_functions: Dictionary[StringName, PackedStringArray] = {}
|
||||
for super_native_class: StringName in super_native_classes:
|
||||
var functions: PackedStringArray = collect_class_functions(super_native_class, existing_funcs)
|
||||
if (functions.is_empty()):
|
||||
continue
|
||||
|
||||
class_to_functions[super_native_class] = functions
|
||||
|
||||
return class_to_functions
|
||||
|
||||
func collect_class_functions(native_class: StringName, existing_funcs: Dictionary[String, bool]):
|
||||
var functions: PackedStringArray = []
|
||||
|
||||
for method: Dictionary in ClassDB.class_get_method_list(native_class, true):
|
||||
if (method[&"flags"] & METHOD_FLAG_VIRTUAL <= 0):
|
||||
continue
|
||||
|
||||
var func_name: String = method[&"name"]
|
||||
if (existing_funcs.has(func_name)):
|
||||
continue
|
||||
|
||||
func_name = create_function_signature(method)
|
||||
functions.append(func_name)
|
||||
|
||||
return functions
|
||||
|
||||
func collect_script_functions(super_class: Script, existing_funcs: Dictionary[String, bool]) -> PackedStringArray:
|
||||
var functions: PackedStringArray = []
|
||||
|
||||
for method: Dictionary in super_class.get_script_method_list():
|
||||
var func_name: String = method[&"name"]
|
||||
if (existing_funcs.has(func_name)):
|
||||
continue
|
||||
|
||||
existing_funcs[func_name] = true
|
||||
|
||||
func_name = create_function_signature(method)
|
||||
functions.append(func_name)
|
||||
|
||||
return functions
|
||||
|
||||
func create_function_signature(method: Dictionary) -> String:
|
||||
var func_name: String = method[&"name"]
|
||||
func_name += "("
|
||||
|
||||
var args: Array = method[&"args"]
|
||||
var default_args: Array = method[&"default_args"]
|
||||
|
||||
var arg_index: int = 0
|
||||
var default_arg_index: int = 0
|
||||
var arg_str: String = ""
|
||||
for arg: Dictionary in args:
|
||||
if (arg_str != ""):
|
||||
arg_str += ", "
|
||||
|
||||
arg_str += arg[&"name"]
|
||||
var type: String = get_type(arg)
|
||||
if (type != ""):
|
||||
arg_str += ": " + type
|
||||
|
||||
if (args.size() - arg_index <= default_args.size()):
|
||||
var default_arg: Variant = default_args[default_arg_index]
|
||||
if (!default_arg):
|
||||
var type_hint: int = arg[&"type"]
|
||||
if (is_dictionary(type_hint)):
|
||||
default_arg = {}
|
||||
elif (is_array(type_hint)):
|
||||
default_arg = []
|
||||
|
||||
arg_str += " = " + var_to_str(default_arg)
|
||||
|
||||
default_arg_index += 1
|
||||
|
||||
arg_index += 1
|
||||
|
||||
func_name += arg_str + ")"
|
||||
|
||||
var return_str: String = get_type(method[&"return"])
|
||||
if (return_str == ""):
|
||||
return_str = "void"
|
||||
|
||||
func_name += " -> " + return_str
|
||||
|
||||
return func_name
|
||||
|
||||
func generate_functions():
|
||||
if (selections.size() == 0):
|
||||
return
|
||||
|
||||
var generated_text: String = ""
|
||||
for function: String in selections.keys():
|
||||
generated_text += "\nfunc " + function + ":\n\tpass\n"
|
||||
|
||||
var editor: CodeEdit = EditorInterface.get_script_editor().get_current_editor().get_base_editor()
|
||||
editor.text += generated_text
|
||||
|
||||
plugin.goto_line(editor.get_line_count() - 1)
|
||||
|
||||
hide()
|
||||
|
||||
func get_type(dict: Dictionary) -> String:
|
||||
var type: String = dict[&"class_name"]
|
||||
if (type != &""):
|
||||
return type
|
||||
|
||||
var type_hint: int = dict[&"type"]
|
||||
if (type_hint == 0):
|
||||
return &""
|
||||
|
||||
type = type_string(type_hint)
|
||||
|
||||
if (is_dictionary(type_hint)):
|
||||
var generic: String = dict[&"hint_string"]
|
||||
if (generic != &""):
|
||||
var generic_parts: PackedStringArray = generic.split(";")
|
||||
if (generic_parts.size() == 2):
|
||||
return type + "[" + generic_parts[0] + ", " + generic_parts[1] + "]"
|
||||
|
||||
if (is_array(type_hint)):
|
||||
var generic: String = dict[&"hint_string"]
|
||||
if (generic != &""):
|
||||
return type + "[" + generic + "]"
|
||||
|
||||
return type
|
||||
|
||||
func is_dictionary(type_hint: int):
|
||||
return type_hint == 27
|
||||
|
||||
func is_array(type_hint: int):
|
||||
return type_hint == 28
|
||||
1
addons/script-ide/override/override_panel.gd.uid
Normal file
1
addons/script-ide/override/override_panel.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://btgbaikmlk6my
|
||||
58
addons/script-ide/override/override_panel.tscn
Normal file
58
addons/script-ide/override/override_panel.tscn
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://bb1n82qxlqanh"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://chc6jwpxj1ig8" path="res://addons/script-ide/override/override_panel.gd" id="1_c3eqr"]
|
||||
|
||||
[node name="OverridePanel" type="PopupPanel"]
|
||||
size = Vector2i(551, 194)
|
||||
script = ExtResource("1_c3eqr")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
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="MarginContainer" type="MarginContainer" parent="PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Select Functions to Override/Implement"
|
||||
|
||||
[node name="FilterTxt" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
placeholder_text = "Filter Methods"
|
||||
|
||||
[node name="ClassFuncTree" type="Tree" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
hide_root = true
|
||||
select_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
alignment = 2
|
||||
|
||||
[node name="OkBtn" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(64, 0)
|
||||
layout_mode = 2
|
||||
text = "Ok"
|
||||
|
||||
[node name="CancelBtn" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(64, 0)
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
Loading…
Add table
Add a link
Reference in a new issue