diff options
Diffstat (limited to 'editor/editor_help.cpp')
-rw-r--r-- | editor/editor_help.cpp | 244 |
1 files changed, 242 insertions, 2 deletions
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index b3bcb9f014..588b657d0d 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -38,6 +38,7 @@ #include "doc_data_compressed.gen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" +#include "editor/editor_property_name_processor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -2587,7 +2588,7 @@ DocTools *EditorHelp::get_doc_data() { return doc; } -//// EditorHelpBit /// +/// EditorHelpBit /// void EditorHelpBit::_go_to_help(String p_what) { EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); @@ -2620,6 +2621,179 @@ void EditorHelpBit::_meta_clicked(String p_select) { } } +String EditorHelpBit::get_class_description(const StringName &p_class_name) const { + if (doc_class_cache.has(p_class_name)) { + return doc_class_cache[p_class_name]; + } + + String description; + HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name); + if (E) { + // Non-native class shouldn't be cached, nor translated. + bool is_native = ClassDB::class_exists(p_class_name); + description = is_native ? DTR(E->value.brief_description) : E->value.brief_description; + + if (is_native) { + doc_class_cache[p_class_name] = description; + } + } + + return description; +} + +String EditorHelpBit::get_property_description(const StringName &p_class_name, const StringName &p_property_name) const { + if (doc_property_cache.has(p_class_name) && doc_property_cache[p_class_name].has(p_property_name)) { + return doc_property_cache[p_class_name][p_property_name]; + } + + String description; + // Non-native properties shouldn't be cached, nor translated. + bool is_native = ClassDB::class_exists(p_class_name); + DocTools *dd = EditorHelp::get_doc_data(); + HashMap<String, DocData::ClassDoc>::ConstIterator E = dd->class_list.find(p_class_name); + if (E) { + for (int i = 0; i < E->value.properties.size(); i++) { + String description_current = is_native ? DTR(E->value.properties[i].description) : E->value.properties[i].description; + + const Vector<String> class_enum = E->value.properties[i].enumeration.split("."); + const String enum_name = class_enum.size() >= 2 ? class_enum[1] : ""; + if (!enum_name.is_empty()) { + // Classes can use enums from other classes, so check from which it came. + HashMap<String, DocData::ClassDoc>::ConstIterator enum_class = dd->class_list.find(class_enum[0]); + if (enum_class) { + for (DocData::ConstantDoc val : enum_class->value.constants) { + // Don't display `_MAX` enum value descriptions, as these are never exposed in the inspector. + if (val.enumeration == enum_name && !val.name.ends_with("_MAX")) { + const String enum_value = EditorPropertyNameProcessor::get_singleton()->process_name(val.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED); + const String enum_prefix = EditorPropertyNameProcessor::get_singleton()->process_name(enum_name, EditorPropertyNameProcessor::STYLE_CAPITALIZED) + " "; + const String enum_description = is_native ? DTR(val.description) : val.description; + + // Prettify the enum value display, so that "<ENUM NAME>_<VALUE>" becomes "Value". + description_current = description_current.trim_prefix("\n") + vformat("\n[b]%s:[/b] %s", enum_value.trim_prefix(enum_prefix), enum_description.is_empty() ? ("[i]" + DTR("No description available.") + "[/i]") : enum_description); + } + } + } + } + + if (E->value.properties[i].name == p_property_name) { + description = description_current; + + if (!is_native) { + break; + } + } + + if (is_native) { + doc_property_cache[p_class_name][E->value.properties[i].name] = description_current; + } + } + } + + return description; +} + +String EditorHelpBit::get_method_description(const StringName &p_class_name, const StringName &p_method_name) const { + if (doc_method_cache.has(p_class_name) && doc_method_cache[p_class_name].has(p_method_name)) { + return doc_method_cache[p_class_name][p_method_name]; + } + + String description; + HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name); + if (E) { + // Non-native methods shouldn't be cached, nor translated. + bool is_native = ClassDB::class_exists(p_class_name); + + for (int i = 0; i < E->value.methods.size(); i++) { + String description_current = is_native ? DTR(E->value.methods[i].description) : E->value.methods[i].description; + + if (E->value.methods[i].name == p_method_name) { + description = description_current; + + if (!is_native) { + break; + } + } + + if (is_native) { + doc_method_cache[p_class_name][E->value.methods[i].name] = description_current; + } + } + } + + return description; +} + +String EditorHelpBit::get_signal_description(const StringName &p_class_name, const StringName &p_signal_name) const { + if (doc_signal_cache.has(p_class_name) && doc_signal_cache[p_class_name].has(p_signal_name)) { + return doc_signal_cache[p_class_name][p_signal_name]; + } + + String description; + HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name); + if (E) { + // Non-native signals shouldn't be cached, nor translated. + bool is_native = ClassDB::class_exists(p_class_name); + + for (int i = 0; i < E->value.signals.size(); i++) { + String description_current = is_native ? DTR(E->value.signals[i].description) : E->value.signals[i].description; + + if (E->value.signals[i].name == p_signal_name) { + description = description_current; + + if (!is_native) { + break; + } + } + + if (is_native) { + doc_signal_cache[p_class_name][E->value.signals[i].name] = description_current; + } + } + } + + return description; +} + +String EditorHelpBit::get_theme_item_description(const StringName &p_class_name, const StringName &p_theme_item_name) const { + if (doc_theme_item_cache.has(p_class_name) && doc_theme_item_cache[p_class_name].has(p_theme_item_name)) { + return doc_theme_item_cache[p_class_name][p_theme_item_name]; + } + + String description; + bool found = false; + DocTools *dd = EditorHelp::get_doc_data(); + HashMap<String, DocData::ClassDoc>::ConstIterator E = dd->class_list.find(p_class_name); + while (E) { + // Non-native theme items shouldn't be cached, nor translated. + bool is_native = ClassDB::class_exists(p_class_name); + + for (int i = 0; i < E->value.theme_properties.size(); i++) { + String description_current = is_native ? DTR(E->value.theme_properties[i].description) : E->value.theme_properties[i].description; + + if (E->value.theme_properties[i].name == p_theme_item_name) { + description = description_current; + found = true; + + if (!is_native) { + break; + } + } + + if (is_native) { + doc_theme_item_cache[p_class_name][E->value.theme_properties[i].name] = description_current; + } + } + + if (found || E->value.inherits.is_empty()) { + break; + } + // Check for inherited theme items. + E = dd->class_list.find(E->value.inherits); + } + + return description; +} + void EditorHelpBit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &EditorHelpBit::set_text); ADD_SIGNAL(MethodInfo("request_hide")); @@ -2650,7 +2824,73 @@ EditorHelpBit::EditorHelpBit() { set_custom_minimum_size(Size2(0, 50 * EDSCALE)); } -//// FindBar /// +/// EditorHelpTooltip /// + +void EditorHelpTooltip::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + if (!tooltip_text.is_empty()) { + parse_tooltip(tooltip_text); + } + } break; + } +} + +// `p_text` is expected to be something like these: +// - `class|Control||`; +// - `property|Control|size|`; +// - `signal|Control|gui_input|(event: InputEvent)` +void EditorHelpTooltip::parse_tooltip(const String &p_text) { + tooltip_text = p_text; + + PackedStringArray slices = p_text.split("|", true, 3); + ERR_FAIL_COND_MSG(slices.size() < 4, "Invalid tooltip formatting. The expect string should be formatted as 'type|class|property|args'."); + + String type = slices[0]; + String class_name = slices[1]; + String property_name = slices[2]; + String property_args = slices[3]; + + String title; + String description; + String formatted_text; + + if (type == "class") { + title = class_name; + description = get_class_description(class_name); + formatted_text = TTR("Class:"); + } else { + title = property_name; + + if (type == "property") { + description = get_property_description(class_name, property_name); + formatted_text = TTR("Property:"); + } else if (type == "method") { + description = get_method_description(class_name, property_name); + formatted_text = TTR("Method:"); + } else if (type == "signal") { + description = get_signal_description(class_name, property_name); + formatted_text = TTR("Signal:"); + } else if (type == "theme_item") { + description = get_theme_item_description(class_name, property_name); + formatted_text = TTR("Theme Item:"); + } else { + ERR_FAIL_MSG("Invalid tooltip type '" + type + "'. Valid types are 'class', 'property', 'method', 'signal', and 'theme_item'."); + } + } + + formatted_text += " [u][b]" + title + "[/b][/u]" + property_args + "\n"; + formatted_text += description.is_empty() ? "[i]" + TTR("No description available.") + "[/i]" : description; + set_text(formatted_text); +} + +EditorHelpTooltip::EditorHelpTooltip(const String &p_text) { + tooltip_text = p_text; + + get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 0)); +} + +/// FindBar /// FindBar::FindBar() { search_text = memnew(LineEdit); |