Editor: Improve Signal Dock for script classes

* Add signal documentation for script classes.
* Use separate sections for script class inheritance.
This commit is contained in:
Danil Alexeev 2023-08-08 12:45:59 +03:00
parent 90f90cbcb0
commit 1d5539cf77
No known key found for this signature in database
GPG key ID: 124453E157DA8DC7

View file

@ -31,6 +31,7 @@
#include "connections_dialog.h" #include "connections_dialog.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/templates/hash_set.h"
#include "editor/doc_tools.h" #include "editor/doc_tools.h"
#include "editor/editor_help.h" #include "editor/editor_help.h"
#include "editor/editor_inspector.h" #include "editor/editor_inspector.h"
@ -1239,60 +1240,102 @@ void ConnectionsDock::update_tree() {
} }
TreeItem *root = tree->create_item(); TreeItem *root = tree->create_item();
DocTools *doc_data = EditorHelp::get_doc_data();
EditorData &editor_data = EditorNode::get_editor_data();
StringName native_base = selected_node->get_class();
Ref<Script> script_base = selected_node->get_script();
List<MethodInfo> node_signals; while (native_base != StringName()) {
String class_name;
String doc_class_name;
Ref<Texture2D> class_icon;
List<MethodInfo> class_signals;
const DocData::ClassDoc *class_doc = nullptr;
selected_node->get_signal_list(&node_signals); if (script_base.is_valid()) {
// Try script global name.
bool did_script = false; class_name = script_base->get_global_name();
StringName base = selected_node->get_class(); if (!class_name.is_empty()) {
HashMap<String, DocData::ClassDoc>::ConstIterator F = doc_data->class_list.find(class_name);
while (base) { if (F) {
List<MethodInfo> node_signals2; class_doc = &F->value;
Ref<Texture2D> icon; doc_class_name = class_name;
String name;
if (!did_script) {
// Get script signals (including signals from any base scripts).
Ref<Script> scr = selected_node->get_script();
if (scr.is_valid()) {
scr->get_script_signal_list(&node_signals2);
if (scr->get_path().is_resource_file()) {
name = scr->get_path().get_file();
} else {
name = scr->get_class();
}
if (has_theme_icon(scr->get_class(), SNAME("EditorIcons"))) {
icon = get_theme_icon(scr->get_class(), SNAME("EditorIcons"));
} }
} }
// Try script path.
if (class_name.is_empty()) {
class_name = script_base->get_path().get_file();
}
if (!class_doc) {
doc_class_name = script_base->get_path().trim_prefix("res://").quote();
HashMap<String, DocData::ClassDoc>::ConstIterator F = doc_data->class_list.find(doc_class_name);
if (F) {
class_doc = &F->value;
}
}
class_icon = editor_data.get_script_icon(script_base);
if (class_icon.is_null() && has_theme_icon(native_base, SNAME("EditorIcons"))) {
class_icon = get_theme_icon(native_base, SNAME("EditorIcons"));
}
script_base->get_script_signal_list(&class_signals);
// TODO: Core: Add optional parameter to ignore base classes (no_inheritance like in ClassDB).
Ref<Script> base = script_base->get_base_script();
if (base.is_valid()) {
List<MethodInfo> base_signals;
base->get_script_signal_list(&base_signals);
HashSet<String> base_signal_names;
for (List<MethodInfo>::Element *F = base_signals.front(); F; F = F->next()) {
base_signal_names.insert(F->get().name);
}
for (List<MethodInfo>::Element *F = class_signals.front(); F; F = F->next()) {
if (base_signal_names.has(F->get().name)) {
class_signals.erase(F);
}
}
}
script_base = base;
} else { } else {
ClassDB::get_signal_list(base, &node_signals2, true); class_name = native_base;
if (has_theme_icon(base, SNAME("EditorIcons"))) {
icon = get_theme_icon(base, SNAME("EditorIcons")); if (has_theme_icon(native_base, SNAME("EditorIcons"))) {
class_icon = get_theme_icon(native_base, SNAME("EditorIcons"));
} }
name = base;
ClassDB::get_signal_list(native_base, &class_signals, true);
HashMap<String, DocData::ClassDoc>::ConstIterator F = doc_data->class_list.find(class_name);
if (F) {
class_doc = &F->value;
doc_class_name = class_name;
}
native_base = ClassDB::get_parent_class(native_base);
} }
if (icon.is_null()) { if (class_icon.is_null()) {
icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons")); class_icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
} }
TreeItem *section_item = nullptr; TreeItem *section_item = nullptr;
// Create subsections. // Create subsections.
if (node_signals2.size()) { if (!class_signals.is_empty()) {
class_signals.sort();
section_item = tree->create_item(root); section_item = tree->create_item(root);
section_item->set_text(0, name); section_item->set_text(0, class_name);
section_item->set_icon(0, icon); section_item->set_icon(0, class_icon);
section_item->set_selectable(0, false); section_item->set_selectable(0, false);
section_item->set_editable(0, false); section_item->set_editable(0, false);
section_item->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), SNAME("Editor"))); section_item->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
node_signals2.sort();
} }
for (MethodInfo &mi : node_signals2) { for (MethodInfo &mi : class_signals) {
const StringName signal_name = mi.name; const StringName signal_name = mi.name;
if (!search_box->get_text().is_subsequence_ofn(signal_name)) { if (!search_box->get_text().is_subsequence_ofn(signal_name)) {
continue; continue;
@ -1320,9 +1363,9 @@ void ConnectionsDock::update_tree() {
String descr; String descr;
bool found = false; bool found = false;
HashMap<StringName, HashMap<StringName, String>>::Iterator G = descr_cache.find(base); HashMap<StringName, HashMap<StringName, String>>::ConstIterator G = descr_cache.find(doc_class_name);
if (G) { if (G) {
HashMap<StringName, String>::Iterator F = G->value.find(signal_name); HashMap<StringName, String>::ConstIterator F = G->value.find(signal_name);
if (F) { if (F) {
found = true; found = true;
descr = F->value; descr = F->value;
@ -1330,22 +1373,15 @@ void ConnectionsDock::update_tree() {
} }
if (!found) { if (!found) {
DocTools *dd = EditorHelp::get_doc_data(); if (class_doc) {
HashMap<String, DocData::ClassDoc>::Iterator F = dd->class_list.find(base); for (int i = 0; i < class_doc->signals.size(); i++) {
while (F && descr.is_empty()) { if (class_doc->signals[i].name == signal_name.operator String()) {
for (int i = 0; i < F->value.signals.size(); i++) { descr = DTR(class_doc->signals[i].description);
if (F->value.signals[i].name == signal_name.operator String()) {
descr = DTR(F->value.signals[i].description);
break; break;
} }
} }
if (!F->value.inherits.is_empty()) {
F = dd->class_list.find(F->value.inherits);
} else {
break;
}
} }
descr_cache[base][signal_name] = descr; descr_cache[doc_class_name][signal_name] = descr;
} }
// "::" separators used in make_custom_tooltip for formatting. // "::" separators used in make_custom_tooltip for formatting.
@ -1400,12 +1436,6 @@ void ConnectionsDock::update_tree() {
} }
} }
} }
if (!did_script) {
did_script = true;
} else {
base = ClassDB::get_parent_class(base);
}
} }
connect_button->set_text(TTR("Connect...")); connect_button->set_text(TTR("Connect..."));