fd6453c45e
This reverts commit 4b817a565c
.
Fixes #64988.
Fixes #64997.
This caused several regressions (#64988, #64997,
https://github.com/godotengine/godot/issues/64997#issuecomment-1229970605)
which point at a flaw in the current logic:
- `Control::NOTIFICATION_ENTER_TREE` triggers a *deferred* notification with
`NOTIFCATION_THEME_CHANGED` as introduced in #62845.
- Some classes use their `THEME_CHANGED` to cache theme items in
member variables (e.g. `style_normal`, etc.), and use those member
variables in `ENTER_TREE`, `READY`, `DRAW`, etc. Since the `THEME_CHANGE`
notification is now deferred, they end up accessing invalid state and this
can lead to not applying theme properly (e.g. for EditorHelp) or crashing
(e.g. for EditorLog or CodeEdit).
So we need to go back to the drawing board and see if `THEME_CHANGED` can be
called earlier so that the previous logic still works?
Or can we refactor all engine code to make sure that:
- `ENTER_TREE` and similar do not depend on theme properties cached in member
variables.
- Or `THEME_CHANGE` does trigger a general UI update to make sure that any
bad theme handling in `ENTER_TREE` and co. gets fixed when `THEME_CHANGE`
does arrive for the first time. But that means having a temporary invalid
(and possibly still crashing) state, and doing some computations twice
which might be heavy (e.g. `EditorHelp::_update_doc()`).
2403 lines
73 KiB
C++
2403 lines
73 KiB
C++
/*************************************************************************/
|
|
/* editor_help.cpp */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* https://godotengine.org */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/*************************************************************************/
|
|
|
|
#include "editor_help.h"
|
|
|
|
#include "core/core_constants.h"
|
|
#include "core/input/input.h"
|
|
#include "core/os/keyboard.h"
|
|
#include "core/version_generated.gen.h"
|
|
#include "doc_data_compressed.gen.h"
|
|
#include "editor/editor_node.h"
|
|
#include "editor/editor_scale.h"
|
|
#include "editor/editor_settings.h"
|
|
#include "editor/plugins/script_editor_plugin.h"
|
|
|
|
#define CONTRIBUTE_URL vformat("%s/community/contributing/updating_the_class_reference.html", VERSION_DOCS_URL)
|
|
|
|
DocTools *EditorHelp::doc = nullptr;
|
|
|
|
void EditorHelp::_update_theme() {
|
|
text_color = get_theme_color(SNAME("text_color"), SNAME("EditorHelp"));
|
|
title_color = get_theme_color(SNAME("title_color"), SNAME("EditorHelp"));
|
|
headline_color = get_theme_color(SNAME("headline_color"), SNAME("EditorHelp"));
|
|
comment_color = get_theme_color(SNAME("comment_color"), SNAME("EditorHelp"));
|
|
symbol_color = get_theme_color(SNAME("symbol_color"), SNAME("EditorHelp"));
|
|
value_color = get_theme_color(SNAME("value_color"), SNAME("EditorHelp"));
|
|
qualifier_color = get_theme_color(SNAME("qualifier_color"), SNAME("EditorHelp"));
|
|
type_color = get_theme_color(SNAME("type_color"), SNAME("EditorHelp"));
|
|
|
|
class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp")));
|
|
class_desc->add_theme_constant_override("line_separation", get_theme_constant(SNAME("line_separation"), SNAME("EditorHelp")));
|
|
class_desc->add_theme_constant_override("table_h_separation", get_theme_constant(SNAME("table_h_separation"), SNAME("EditorHelp")));
|
|
class_desc->add_theme_constant_override("table_v_separation", get_theme_constant(SNAME("table_v_separation"), SNAME("EditorHelp")));
|
|
|
|
doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
|
|
doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
|
|
doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts"));
|
|
doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
|
|
|
|
doc_title_font_size = get_theme_font_size(SNAME("doc_title_size"), SNAME("EditorFonts"));
|
|
}
|
|
|
|
void EditorHelp::_search(bool p_search_previous) {
|
|
if (p_search_previous) {
|
|
find_bar->search_prev();
|
|
} else {
|
|
find_bar->search_next();
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_class_desc_finished() {
|
|
if (scroll_to >= 0) {
|
|
class_desc->scroll_to_paragraph(scroll_to);
|
|
}
|
|
scroll_to = -1;
|
|
}
|
|
|
|
void EditorHelp::_class_list_select(const String &p_select) {
|
|
_goto_desc(p_select);
|
|
}
|
|
|
|
void EditorHelp::_class_desc_select(const String &p_select) {
|
|
if (p_select.begins_with("$")) { //enum
|
|
String select = p_select.substr(1, p_select.length());
|
|
String class_name;
|
|
if (select.contains(".")) {
|
|
class_name = select.get_slice(".", 0);
|
|
select = select.get_slice(".", 1);
|
|
} else {
|
|
class_name = "@GlobalScope";
|
|
}
|
|
emit_signal(SNAME("go_to_help"), "class_enum:" + class_name + ":" + select);
|
|
return;
|
|
} else if (p_select.begins_with("#")) {
|
|
emit_signal(SNAME("go_to_help"), "class_name:" + p_select.substr(1, p_select.length()));
|
|
return;
|
|
} else if (p_select.begins_with("@")) {
|
|
int tag_end = p_select.find(" ");
|
|
|
|
String tag = p_select.substr(1, tag_end - 1);
|
|
String link = p_select.substr(tag_end + 1, p_select.length()).lstrip(" ");
|
|
|
|
String topic;
|
|
HashMap<String, int> *table = nullptr;
|
|
|
|
if (tag == "method") {
|
|
topic = "class_method";
|
|
table = &this->method_line;
|
|
} else if (tag == "member") {
|
|
topic = "class_property";
|
|
table = &this->property_line;
|
|
} else if (tag == "enum") {
|
|
topic = "class_enum";
|
|
table = &this->enum_line;
|
|
} else if (tag == "signal") {
|
|
topic = "class_signal";
|
|
table = &this->signal_line;
|
|
} else if (tag == "constant") {
|
|
topic = "class_constant";
|
|
table = &this->constant_line;
|
|
} else if (tag == "annotation") {
|
|
topic = "class_annotation";
|
|
table = &this->annotation_line;
|
|
} else if (tag == "theme_item") {
|
|
topic = "theme_item";
|
|
table = &this->theme_property_line;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
// Case order is important here to correctly handle edge cases like Variant.Type in @GlobalScope.
|
|
if (table->has(link)) {
|
|
// Found in the current page.
|
|
if (class_desc->is_ready()) {
|
|
class_desc->scroll_to_paragraph((*table)[link]);
|
|
} else {
|
|
scroll_to = (*table)[link];
|
|
}
|
|
} else {
|
|
// Look for link in @GlobalScope.
|
|
// Note that a link like @GlobalScope.enum_name will not be found in this section, only enum_name will be.
|
|
if (topic == "class_enum") {
|
|
const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"];
|
|
|
|
for (int i = 0; i < cd.constants.size(); i++) {
|
|
if (cd.constants[i].enumeration == link) {
|
|
// Found in @GlobalScope.
|
|
emit_signal(SNAME("go_to_help"), topic + ":@GlobalScope:" + link);
|
|
return;
|
|
}
|
|
}
|
|
} else if (topic == "class_constant") {
|
|
const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"];
|
|
|
|
for (int i = 0; i < cd.constants.size(); i++) {
|
|
if (cd.constants[i].name == link) {
|
|
// Found in @GlobalScope.
|
|
emit_signal(SNAME("go_to_help"), topic + ":@GlobalScope:" + link);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (link.contains(".")) {
|
|
int class_end = link.find(".");
|
|
emit_signal(SNAME("go_to_help"), topic + ":" + link.substr(0, class_end) + ":" + link.substr(class_end + 1, link.length()));
|
|
}
|
|
}
|
|
} else if (p_select.begins_with("http")) {
|
|
OS::get_singleton()->shell_open(p_select);
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_class_desc_input(const Ref<InputEvent> &p_input) {
|
|
}
|
|
|
|
void EditorHelp::_class_desc_resized(bool p_force_update_theme) {
|
|
// Add extra horizontal margins for better readability.
|
|
// The margins increase as the width of the editor help container increases.
|
|
Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
|
|
int font_size = get_theme_font_size(SNAME("doc_source_size"), SNAME("EditorFonts"));
|
|
real_t char_width = doc_code_font->get_char_size('x', font_size).width;
|
|
const int new_display_margin = MAX(30 * EDSCALE, get_parent_anchorable_rect().size.width - char_width * 120 * EDSCALE) * 0.5;
|
|
if (display_margin != new_display_margin || p_force_update_theme) {
|
|
display_margin = new_display_margin;
|
|
|
|
Ref<StyleBox> class_desc_stylebox = EditorNode::get_singleton()->get_theme_base()->get_theme_stylebox(SNAME("normal"), SNAME("RichTextLabel"))->duplicate();
|
|
class_desc_stylebox->set_default_margin(SIDE_LEFT, display_margin);
|
|
class_desc_stylebox->set_default_margin(SIDE_RIGHT, display_margin);
|
|
class_desc->add_theme_style_override("normal", class_desc_stylebox);
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
|
|
String t = p_type;
|
|
if (t.is_empty()) {
|
|
t = "void";
|
|
}
|
|
bool can_ref = (t != "void" && !t.contains("*")) || !p_enum.is_empty();
|
|
|
|
if (!p_enum.is_empty()) {
|
|
if (p_enum.get_slice_count(".") > 1) {
|
|
t = p_enum.get_slice(".", 1);
|
|
} else {
|
|
t = p_enum.get_slice(".", 0);
|
|
}
|
|
}
|
|
|
|
class_desc->push_color(type_color);
|
|
bool add_array = false;
|
|
if (can_ref) {
|
|
if (t.ends_with("[]")) {
|
|
add_array = true;
|
|
t = t.replace("[]", "");
|
|
}
|
|
if (p_enum.is_empty()) {
|
|
class_desc->push_meta("#" + t); //class
|
|
} else {
|
|
class_desc->push_meta("$" + p_enum); //class
|
|
}
|
|
}
|
|
class_desc->add_text(t);
|
|
if (can_ref) {
|
|
class_desc->pop();
|
|
if (add_array) {
|
|
class_desc->add_text(" ");
|
|
class_desc->push_meta("#Array"); //class
|
|
class_desc->add_text("[]");
|
|
class_desc->pop();
|
|
}
|
|
}
|
|
class_desc->pop();
|
|
}
|
|
|
|
void EditorHelp::_add_type_icon(const String &p_type, int p_size) {
|
|
Ref<Texture2D> icon;
|
|
if (has_theme_icon(p_type, SNAME("EditorIcons"))) {
|
|
icon = get_theme_icon(p_type, SNAME("EditorIcons"));
|
|
} else if (ClassDB::class_exists(p_type) && ClassDB::is_parent_class(p_type, "Object")) {
|
|
icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
|
|
} else {
|
|
icon = get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons"));
|
|
}
|
|
|
|
Vector2i size = Vector2i(icon->get_width(), icon->get_height());
|
|
if (p_size > 0) {
|
|
// Ensures icon scales proportionally on both axis, based on icon height.
|
|
float ratio = p_size / float(size.height);
|
|
size.width *= ratio;
|
|
size.height *= ratio;
|
|
}
|
|
|
|
class_desc->add_image(icon, size.width, size.height);
|
|
}
|
|
|
|
String EditorHelp::_fix_constant(const String &p_constant) const {
|
|
if (p_constant.strip_edges() == "4294967295") {
|
|
return "0xFFFFFFFF";
|
|
}
|
|
|
|
if (p_constant.strip_edges() == "2147483647") {
|
|
return "0x7FFFFFFF";
|
|
}
|
|
|
|
if (p_constant.strip_edges() == "1048575") {
|
|
return "0xFFFFF";
|
|
}
|
|
|
|
return p_constant;
|
|
}
|
|
|
|
void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) {
|
|
method_line[p_method.name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
|
|
|
|
const bool is_vararg = p_method.qualifiers.contains("vararg");
|
|
|
|
if (p_overview) {
|
|
class_desc->push_cell();
|
|
class_desc->push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT, Control::TEXT_DIRECTION_AUTO, "");
|
|
} else {
|
|
_add_bulletpoint();
|
|
}
|
|
|
|
_add_type(p_method.return_type, p_method.return_enum);
|
|
|
|
if (p_overview) {
|
|
class_desc->pop(); //align
|
|
class_desc->pop(); //cell
|
|
class_desc->push_cell();
|
|
} else {
|
|
class_desc->add_text(" ");
|
|
}
|
|
|
|
if (p_overview && !p_method.description.strip_edges().is_empty()) {
|
|
class_desc->push_meta("@method " + p_method.name);
|
|
}
|
|
|
|
class_desc->push_color(headline_color);
|
|
_add_text(p_method.name);
|
|
class_desc->pop();
|
|
|
|
if (p_overview && !p_method.description.strip_edges().is_empty()) {
|
|
class_desc->pop(); //meta
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("(");
|
|
class_desc->pop();
|
|
|
|
for (int j = 0; j < p_method.arguments.size(); j++) {
|
|
class_desc->push_color(text_color);
|
|
if (j > 0) {
|
|
class_desc->add_text(", ");
|
|
}
|
|
|
|
_add_text(p_method.arguments[j].name);
|
|
class_desc->add_text(": ");
|
|
_add_type(p_method.arguments[j].type, p_method.arguments[j].enumeration);
|
|
if (!p_method.arguments[j].default_value.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" = ");
|
|
class_desc->pop();
|
|
class_desc->push_color(value_color);
|
|
_add_text(_fix_constant(p_method.arguments[j].default_value));
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->pop();
|
|
}
|
|
|
|
if (is_vararg) {
|
|
class_desc->push_color(text_color);
|
|
if (p_method.arguments.size()) {
|
|
class_desc->add_text(", ");
|
|
}
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("...");
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(")");
|
|
class_desc->pop();
|
|
if (!p_method.qualifiers.is_empty()) {
|
|
class_desc->push_color(qualifier_color);
|
|
class_desc->add_text(" ");
|
|
_add_text(p_method.qualifiers);
|
|
class_desc->pop();
|
|
}
|
|
|
|
if (p_overview) {
|
|
class_desc->pop(); //cell
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_add_bulletpoint() {
|
|
static const char32_t prefix[3] = { 0x25CF /* filled circle */, ' ', 0 };
|
|
class_desc->add_text(String(prefix));
|
|
}
|
|
|
|
Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
|
|
if (!doc->class_list.has(p_class)) {
|
|
return ERR_DOES_NOT_EXIST;
|
|
}
|
|
|
|
select_locked = true;
|
|
|
|
class_desc->show();
|
|
|
|
description_line = 0;
|
|
|
|
if (p_class == edited_class) {
|
|
return OK; // Already there.
|
|
}
|
|
|
|
edited_class = p_class;
|
|
_update_doc();
|
|
return OK;
|
|
}
|
|
|
|
void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods, bool &r_method_descrpitons) {
|
|
Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
|
|
class_desc->pop(); // title font size
|
|
class_desc->pop(); // title font
|
|
class_desc->pop(); // title color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_indent(1);
|
|
class_desc->push_table(2);
|
|
class_desc->set_table_column_expand(1, true);
|
|
|
|
bool any_previous = false;
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
Vector<DocData::MethodDoc> m;
|
|
|
|
for (int i = 0; i < p_methods.size(); i++) {
|
|
const String &q = p_methods[i].qualifiers;
|
|
if ((pass == 0 && q.contains("virtual")) || (pass == 1 && !q.contains("virtual"))) {
|
|
m.push_back(p_methods[i]);
|
|
}
|
|
}
|
|
|
|
if (any_previous && !m.is_empty()) {
|
|
class_desc->push_cell();
|
|
class_desc->pop(); //cell
|
|
class_desc->push_cell();
|
|
class_desc->pop(); //cell
|
|
}
|
|
|
|
String group_prefix;
|
|
for (int i = 0; i < m.size(); i++) {
|
|
const String new_prefix = m[i].name.substr(0, 3);
|
|
bool is_new_group = false;
|
|
|
|
if (i < m.size() - 1 && new_prefix == m[i + 1].name.substr(0, 3) && new_prefix != group_prefix) {
|
|
is_new_group = i > 0;
|
|
group_prefix = new_prefix;
|
|
} else if (!group_prefix.is_empty() && new_prefix != group_prefix) {
|
|
is_new_group = true;
|
|
group_prefix = "";
|
|
}
|
|
|
|
if (is_new_group && pass == 1) {
|
|
class_desc->push_cell();
|
|
class_desc->pop(); //cell
|
|
class_desc->push_cell();
|
|
class_desc->pop(); //cell
|
|
}
|
|
|
|
if (!m[i].description.strip_edges().is_empty() || m[i].errors_returned.size() > 0) {
|
|
r_method_descrpitons = true;
|
|
}
|
|
|
|
_add_method(m[i], true);
|
|
}
|
|
|
|
any_previous = !m.is_empty();
|
|
}
|
|
|
|
class_desc->pop(); //table
|
|
class_desc->pop();
|
|
class_desc->pop(); // font
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
void EditorHelp::_update_method_descriptions(const DocData::ClassDoc p_classdoc, const Vector<DocData::MethodDoc> p_methods, const String &p_method_type) {
|
|
Ref<Font> doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
|
|
Ref<Font> doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
|
|
Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
|
|
String link_color_text = title_color.to_html(false);
|
|
class_desc->pop(); // title font size
|
|
class_desc->pop(); // title font
|
|
class_desc->pop(); // title color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
Vector<DocData::MethodDoc> methods_filtered;
|
|
|
|
for (int i = 0; i < p_methods.size(); i++) {
|
|
const String &q = p_methods[i].qualifiers;
|
|
if ((pass == 0 && q.contains("virtual")) || (pass == 1 && !q.contains("virtual"))) {
|
|
methods_filtered.push_back(p_methods[i]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < methods_filtered.size(); i++) {
|
|
class_desc->push_font(doc_code_font);
|
|
_add_method(methods_filtered[i], false);
|
|
class_desc->pop();
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
class_desc->push_color(text_color);
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_indent(1);
|
|
if (methods_filtered[i].errors_returned.size()) {
|
|
class_desc->append_text(TTR("Error codes returned:"));
|
|
class_desc->add_newline();
|
|
class_desc->push_list(0, RichTextLabel::LIST_DOTS, false);
|
|
for (int j = 0; j < methods_filtered[i].errors_returned.size(); j++) {
|
|
if (j > 0) {
|
|
class_desc->add_newline();
|
|
}
|
|
int val = methods_filtered[i].errors_returned[j];
|
|
String text = itos(val);
|
|
for (int k = 0; k < CoreConstants::get_global_constant_count(); k++) {
|
|
if (CoreConstants::get_global_constant_value(k) == val && CoreConstants::get_global_constant_enum(k) == SNAME("Error")) {
|
|
text = CoreConstants::get_global_constant_name(k);
|
|
break;
|
|
}
|
|
}
|
|
|
|
class_desc->push_bold();
|
|
class_desc->append_text(text);
|
|
class_desc->pop();
|
|
}
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
if (!methods_filtered[i].description.strip_edges().is_empty()) {
|
|
_add_text(DTR(methods_filtered[i].description));
|
|
} else {
|
|
class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
|
|
class_desc->add_text(" ");
|
|
class_desc->push_color(comment_color);
|
|
if (p_classdoc.is_script_doc) {
|
|
class_desc->append_text(vformat(TTR("There is currently no description for this %s."), p_method_type));
|
|
} else {
|
|
class_desc->append_text(vformat(TTR("There is currently no description for this %s. Please help us by [color=$color][url=$url]contributing one[/url][/color]!"), p_method_type).replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
|
|
}
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_update_doc() {
|
|
if (!doc->class_list.has(edited_class)) {
|
|
return;
|
|
}
|
|
|
|
scroll_locked = true;
|
|
|
|
class_desc->clear();
|
|
method_line.clear();
|
|
section_line.clear();
|
|
|
|
_update_theme();
|
|
String link_color_text = title_color.to_html(false);
|
|
|
|
DocData::ClassDoc cd = doc->class_list[edited_class]; // Make a copy, so we can sort without worrying.
|
|
|
|
// Class name
|
|
section_line.push_back(Pair<String, int>(TTR("Top"), 0));
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->push_color(title_color);
|
|
class_desc->add_text(TTR("Class:") + " ");
|
|
_add_type_icon(edited_class, doc_title_font_size);
|
|
class_desc->add_text(" ");
|
|
class_desc->push_color(headline_color);
|
|
_add_text(edited_class);
|
|
class_desc->pop(); // color
|
|
class_desc->pop(); // color
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->add_newline();
|
|
|
|
// Inheritance tree
|
|
|
|
// Ascendents
|
|
if (!cd.inherits.is_empty()) {
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_font);
|
|
class_desc->add_text(TTR("Inherits:") + " ");
|
|
|
|
String inherits = cd.inherits;
|
|
|
|
while (!inherits.is_empty()) {
|
|
_add_type_icon(inherits);
|
|
class_desc->add_text(" "); // Extra space, otherwise icon borrows hyperlink from _add_type().
|
|
_add_type(inherits);
|
|
|
|
inherits = doc->class_list[inherits].inherits;
|
|
|
|
if (!inherits.is_empty()) {
|
|
class_desc->add_text(" < ");
|
|
}
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Descendents
|
|
if (cd.is_script_doc || ClassDB::class_exists(cd.name)) {
|
|
bool found = false;
|
|
bool prev = false;
|
|
|
|
class_desc->push_font(doc_font);
|
|
for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
|
|
if (E.value.inherits == cd.name) {
|
|
if (!found) {
|
|
class_desc->push_color(title_color);
|
|
class_desc->add_text(TTR("Inherited by:") + " ");
|
|
found = true;
|
|
}
|
|
|
|
if (prev) {
|
|
class_desc->add_text(" , ");
|
|
}
|
|
_add_type_icon(E.value.name);
|
|
class_desc->add_text(" "); // Extra space, otherwise icon borrows hyperlink from _add_type().
|
|
_add_type(E.value.name);
|
|
prev = true;
|
|
}
|
|
}
|
|
class_desc->pop();
|
|
|
|
if (found) {
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
}
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
// Brief description
|
|
if (!cd.brief_description.strip_edges().is_empty()) {
|
|
class_desc->push_color(text_color);
|
|
class_desc->push_font(doc_bold_font);
|
|
class_desc->push_indent(1);
|
|
_add_text(DTR(cd.brief_description));
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Class description
|
|
if (!cd.description.strip_edges().is_empty()) {
|
|
section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_paragraph_count() - 2));
|
|
description_line = class_desc->get_paragraph_count() - 2;
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Description"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
class_desc->push_color(text_color);
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_indent(1);
|
|
_add_text(DTR(cd.description));
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Online tutorials
|
|
if (cd.tutorials.size()) {
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Online Tutorials"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->push_indent(1);
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->add_newline();
|
|
|
|
for (int i = 0; i < cd.tutorials.size(); i++) {
|
|
const String link = DTR(cd.tutorials[i].link);
|
|
String linktxt = (cd.tutorials[i].title.is_empty()) ? link : DTR(cd.tutorials[i].title);
|
|
const int seppos = linktxt.find("//");
|
|
if (seppos != -1) {
|
|
linktxt = link.substr(seppos + 2);
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->append_text("[url=" + link + "]" + linktxt + "[/url]");
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Properties overview
|
|
HashSet<String> skip_methods;
|
|
bool property_descr = false;
|
|
|
|
bool has_properties = cd.properties.size() != 0;
|
|
if (cd.is_script_doc) {
|
|
has_properties = false;
|
|
for (int i = 0; i < cd.properties.size(); i++) {
|
|
if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.strip_edges().is_empty()) {
|
|
continue;
|
|
}
|
|
has_properties = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (has_properties) {
|
|
section_line.push_back(Pair<String, int>(TTR("Properties"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Properties"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_indent(1);
|
|
class_desc->push_table(4);
|
|
class_desc->set_table_column_expand(1, true);
|
|
|
|
for (int i = 0; i < cd.properties.size(); i++) {
|
|
// Ignore undocumented private.
|
|
if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.strip_edges().is_empty()) {
|
|
continue;
|
|
}
|
|
property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
|
|
|
|
// Property type.
|
|
class_desc->push_cell();
|
|
class_desc->push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT, Control::TEXT_DIRECTION_AUTO, "");
|
|
class_desc->push_font(doc_code_font);
|
|
_add_type(cd.properties[i].type, cd.properties[i].enumeration);
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop(); // cell
|
|
|
|
bool describe = false;
|
|
|
|
if (!cd.properties[i].setter.is_empty()) {
|
|
skip_methods.insert(cd.properties[i].setter);
|
|
describe = true;
|
|
}
|
|
if (!cd.properties[i].getter.is_empty()) {
|
|
skip_methods.insert(cd.properties[i].getter);
|
|
describe = true;
|
|
}
|
|
|
|
if (!cd.properties[i].description.strip_edges().is_empty()) {
|
|
describe = true;
|
|
}
|
|
|
|
if (cd.properties[i].overridden) {
|
|
describe = false;
|
|
}
|
|
|
|
// Property name.
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_color(headline_color);
|
|
|
|
if (describe) {
|
|
class_desc->push_meta("@member " + cd.properties[i].name);
|
|
}
|
|
|
|
_add_text(cd.properties[i].name);
|
|
|
|
if (describe) {
|
|
class_desc->pop();
|
|
property_descr = true;
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop(); // cell
|
|
|
|
// Property value.
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
|
|
if (!cd.properties[i].default_value.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
if (cd.properties[i].overridden) {
|
|
class_desc->add_text(" [");
|
|
class_desc->push_meta("@member " + cd.properties[i].overrides + "." + cd.properties[i].name);
|
|
_add_text(vformat(TTR("overrides %s:"), cd.properties[i].overrides));
|
|
class_desc->pop();
|
|
class_desc->add_text(" ");
|
|
} else {
|
|
class_desc->add_text(" [" + TTR("default:") + " ");
|
|
}
|
|
class_desc->pop();
|
|
|
|
class_desc->push_color(value_color);
|
|
_add_text(_fix_constant(cd.properties[i].default_value));
|
|
class_desc->pop();
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("]");
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->pop(); // cell
|
|
|
|
// Property setters and getters.
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
|
|
if (cd.is_script_doc && (!cd.properties[i].setter.is_empty() || !cd.properties[i].getter.is_empty())) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" [" + TTR("property:") + " ");
|
|
class_desc->pop(); // color
|
|
|
|
if (!cd.properties[i].setter.is_empty()) {
|
|
class_desc->push_color(value_color);
|
|
class_desc->add_text("setter");
|
|
class_desc->pop(); // color
|
|
}
|
|
if (!cd.properties[i].getter.is_empty()) {
|
|
if (!cd.properties[i].setter.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(", ");
|
|
class_desc->pop(); // color
|
|
}
|
|
class_desc->push_color(value_color);
|
|
class_desc->add_text("getter");
|
|
class_desc->pop(); // color
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("]");
|
|
class_desc->pop(); // color
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->pop(); // cell
|
|
}
|
|
|
|
class_desc->pop(); // table
|
|
class_desc->pop();
|
|
class_desc->pop(); // font
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Methods overview
|
|
bool constructor_descriptions = false;
|
|
bool method_descriptions = false;
|
|
bool operator_descriptions = false;
|
|
bool sort_methods = EditorSettings::get_singleton()->get("text_editor/help/sort_functions_alphabetically");
|
|
|
|
Vector<DocData::MethodDoc> methods;
|
|
|
|
for (int i = 0; i < cd.methods.size(); i++) {
|
|
if (skip_methods.has(cd.methods[i].name)) {
|
|
if (cd.methods[i].arguments.size() == 0 /* getter */ || (cd.methods[i].arguments.size() == 1 && cd.methods[i].return_type == "void" /* setter */)) {
|
|
continue;
|
|
}
|
|
}
|
|
// Ignore undocumented non virtual private.
|
|
if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.strip_edges().is_empty() && !cd.methods[i].qualifiers.contains("virtual")) {
|
|
continue;
|
|
}
|
|
methods.push_back(cd.methods[i]);
|
|
}
|
|
|
|
if (!cd.constructors.is_empty()) {
|
|
if (sort_methods) {
|
|
cd.constructors.sort();
|
|
}
|
|
|
|
section_line.push_back(Pair<String, int>(TTR("Constructors"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Constructors"));
|
|
_update_method_list(cd.constructors, constructor_descriptions);
|
|
}
|
|
|
|
if (!methods.is_empty()) {
|
|
if (sort_methods) {
|
|
methods.sort();
|
|
}
|
|
section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Methods"));
|
|
_update_method_list(methods, method_descriptions);
|
|
}
|
|
|
|
if (!cd.operators.is_empty()) {
|
|
if (sort_methods) {
|
|
cd.operators.sort();
|
|
}
|
|
|
|
section_line.push_back(Pair<String, int>(TTR("Operators"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Operators"));
|
|
_update_method_list(cd.operators, operator_descriptions);
|
|
}
|
|
|
|
// Theme properties
|
|
if (!cd.theme_properties.is_empty()) {
|
|
section_line.push_back(Pair<String, int>(TTR("Theme Properties"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Theme Properties"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
class_desc->push_indent(1);
|
|
|
|
String theme_data_type;
|
|
HashMap<String, String> data_type_names;
|
|
data_type_names["color"] = TTR("Colors");
|
|
data_type_names["constant"] = TTR("Constants");
|
|
data_type_names["font"] = TTR("Fonts");
|
|
data_type_names["font_size"] = TTR("Font Sizes");
|
|
data_type_names["icon"] = TTR("Icons");
|
|
data_type_names["style"] = TTR("Styles");
|
|
|
|
for (int i = 0; i < cd.theme_properties.size(); i++) {
|
|
theme_property_line[cd.theme_properties[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
|
|
|
|
if (theme_data_type != cd.theme_properties[i].data_type) {
|
|
theme_data_type = cd.theme_properties[i].data_type;
|
|
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
if (data_type_names.has(theme_data_type)) {
|
|
class_desc->add_text(data_type_names[theme_data_type]);
|
|
} else {
|
|
class_desc->add_text("");
|
|
}
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Theme item header.
|
|
class_desc->push_font(doc_code_font);
|
|
_add_bulletpoint();
|
|
|
|
// Theme item object type.
|
|
_add_type(cd.theme_properties[i].type);
|
|
|
|
// Theme item name.
|
|
class_desc->push_color(headline_color);
|
|
class_desc->add_text(" ");
|
|
_add_text(cd.theme_properties[i].name);
|
|
class_desc->pop();
|
|
|
|
// Theme item default value.
|
|
if (!cd.theme_properties[i].default_value.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" [" + TTR("default:") + " ");
|
|
class_desc->pop();
|
|
class_desc->push_color(value_color);
|
|
_add_text(_fix_constant(cd.theme_properties[i].default_value));
|
|
class_desc->pop();
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("]");
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->pop(); // monofont
|
|
|
|
// Theme item description.
|
|
if (!cd.theme_properties[i].description.strip_edges().is_empty()) {
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_color(comment_color);
|
|
class_desc->push_indent(1);
|
|
_add_text(DTR(cd.theme_properties[i].description));
|
|
class_desc->pop(); // indent
|
|
class_desc->pop(); // color
|
|
class_desc->pop(); // font
|
|
}
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Signals
|
|
if (!cd.signals.is_empty()) {
|
|
if (sort_methods) {
|
|
cd.signals.sort();
|
|
}
|
|
|
|
section_line.push_back(Pair<String, int>(TTR("Signals"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Signals"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
class_desc->push_indent(1);
|
|
|
|
for (int i = 0; i < cd.signals.size(); i++) {
|
|
signal_line[cd.signals[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
|
|
|
|
class_desc->push_font(doc_code_font); // monofont
|
|
_add_bulletpoint();
|
|
class_desc->push_color(headline_color);
|
|
_add_text(cd.signals[i].name);
|
|
class_desc->pop();
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("(");
|
|
class_desc->pop();
|
|
for (int j = 0; j < cd.signals[i].arguments.size(); j++) {
|
|
class_desc->push_color(text_color);
|
|
if (j > 0) {
|
|
class_desc->add_text(", ");
|
|
}
|
|
|
|
_add_text(cd.signals[i].arguments[j].name);
|
|
class_desc->add_text(": ");
|
|
_add_type(cd.signals[i].arguments[j].type);
|
|
if (!cd.signals[i].arguments[j].default_value.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" = ");
|
|
class_desc->pop();
|
|
_add_text(cd.signals[i].arguments[j].default_value);
|
|
}
|
|
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(")");
|
|
class_desc->pop();
|
|
class_desc->pop(); // end monofont
|
|
if (!cd.signals[i].description.strip_edges().is_empty()) {
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_color(comment_color);
|
|
class_desc->push_indent(1);
|
|
_add_text(DTR(cd.signals[i].description));
|
|
class_desc->pop(); // indent
|
|
class_desc->pop();
|
|
class_desc->pop(); // font
|
|
}
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Constants and enums
|
|
if (!cd.constants.is_empty()) {
|
|
HashMap<String, Vector<DocData::ConstantDoc>> enums;
|
|
Vector<DocData::ConstantDoc> constants;
|
|
|
|
for (int i = 0; i < cd.constants.size(); i++) {
|
|
if (!cd.constants[i].enumeration.is_empty()) {
|
|
if (!enums.has(cd.constants[i].enumeration)) {
|
|
enums[cd.constants[i].enumeration] = Vector<DocData::ConstantDoc>();
|
|
}
|
|
|
|
enums[cd.constants[i].enumeration].push_back(cd.constants[i]);
|
|
} else {
|
|
// Ignore undocumented private.
|
|
if (cd.constants[i].name.begins_with("_") && cd.constants[i].description.strip_edges().is_empty()) {
|
|
continue;
|
|
}
|
|
constants.push_back(cd.constants[i]);
|
|
}
|
|
}
|
|
|
|
// Enums
|
|
if (enums.size()) {
|
|
section_line.push_back(Pair<String, int>(TTR("Enumerations"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Enumerations"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
class_desc->push_indent(1);
|
|
|
|
class_desc->add_newline();
|
|
|
|
for (KeyValue<String, Vector<DocData::ConstantDoc>> &E : enums) {
|
|
enum_line[E.key] = class_desc->get_paragraph_count() - 2;
|
|
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_color(title_color);
|
|
if (E.value.size() && E.value[0].is_bitfield) {
|
|
class_desc->add_text("flags ");
|
|
} else {
|
|
class_desc->add_text("enum ");
|
|
}
|
|
class_desc->pop();
|
|
String e = E.key;
|
|
if ((e.get_slice_count(".") > 1) && (e.get_slice(".", 0) == edited_class)) {
|
|
e = e.get_slice(".", 1);
|
|
}
|
|
|
|
class_desc->push_color(headline_color);
|
|
class_desc->add_text(e);
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(":");
|
|
class_desc->pop();
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
// Enum description.
|
|
if (e != "@unnamed_enums" && cd.enums.has(e)) {
|
|
class_desc->push_color(text_color);
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_indent(1);
|
|
_add_text(cd.enums[e]);
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->push_indent(1);
|
|
Vector<DocData::ConstantDoc> enum_list = E.value;
|
|
|
|
HashMap<String, int> enumValuesContainer;
|
|
int enumStartingLine = enum_line[E.key];
|
|
|
|
for (int i = 0; i < enum_list.size(); i++) {
|
|
if (cd.name == "@GlobalScope") {
|
|
enumValuesContainer[enum_list[i].name] = enumStartingLine;
|
|
}
|
|
|
|
// Add the enum constant line to the constant_line map so we can locate it as a constant.
|
|
constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2;
|
|
|
|
class_desc->push_font(doc_code_font);
|
|
_add_bulletpoint();
|
|
class_desc->push_color(headline_color);
|
|
_add_text(enum_list[i].name);
|
|
class_desc->pop();
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" = ");
|
|
class_desc->pop();
|
|
class_desc->push_color(value_color);
|
|
_add_text(_fix_constant(enum_list[i].value));
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
|
|
class_desc->add_newline();
|
|
|
|
if (!enum_list[i].description.strip_edges().is_empty()) {
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_color(comment_color);
|
|
_add_text(DTR(enum_list[i].description));
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
if (DTR(enum_list[i].description).find("\n") > 0) {
|
|
class_desc->add_newline();
|
|
}
|
|
}
|
|
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
if (cd.name == "@GlobalScope") {
|
|
enum_values_line[E.key] = enumValuesContainer;
|
|
}
|
|
|
|
class_desc->pop();
|
|
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Constants
|
|
if (constants.size()) {
|
|
section_line.push_back(Pair<String, int>(TTR("Constants"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Constants"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
class_desc->push_indent(1);
|
|
|
|
class_desc->add_newline();
|
|
|
|
for (int i = 0; i < constants.size(); i++) {
|
|
constant_line[constants[i].name] = class_desc->get_paragraph_count() - 2;
|
|
class_desc->push_font(doc_code_font);
|
|
|
|
if (constants[i].value.begins_with("Color(") && constants[i].value.ends_with(")")) {
|
|
String stripped = constants[i].value.replace(" ", "").replace("Color(", "").replace(")", "");
|
|
Vector<float> color = stripped.split_floats(",");
|
|
if (color.size() >= 3) {
|
|
class_desc->push_color(Color(color[0], color[1], color[2]));
|
|
_add_bulletpoint();
|
|
class_desc->pop();
|
|
}
|
|
} else {
|
|
_add_bulletpoint();
|
|
}
|
|
|
|
class_desc->push_color(headline_color);
|
|
_add_text(constants[i].name);
|
|
class_desc->pop();
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" = ");
|
|
class_desc->pop();
|
|
class_desc->push_color(value_color);
|
|
_add_text(_fix_constant(constants[i].value));
|
|
class_desc->pop();
|
|
|
|
class_desc->pop();
|
|
|
|
class_desc->add_newline();
|
|
|
|
if (!constants[i].description.strip_edges().is_empty()) {
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_color(comment_color);
|
|
_add_text(DTR(constants[i].description));
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
if (DTR(constants[i].description).find("\n") > 0) {
|
|
class_desc->add_newline();
|
|
}
|
|
}
|
|
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
}
|
|
|
|
// Annotations
|
|
if (!cd.annotations.is_empty()) {
|
|
if (sort_methods) {
|
|
cd.annotations.sort();
|
|
}
|
|
|
|
section_line.push_back(Pair<String, int>(TTR("Annotations"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Annotations"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
class_desc->push_indent(1);
|
|
|
|
for (int i = 0; i < cd.annotations.size(); i++) {
|
|
annotation_line[cd.annotations[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
|
|
|
|
class_desc->push_font(doc_code_font); // monofont
|
|
_add_bulletpoint();
|
|
class_desc->push_color(headline_color);
|
|
_add_text(cd.annotations[i].name);
|
|
class_desc->pop();
|
|
|
|
if (cd.annotations[i].arguments.size() > 0) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("(");
|
|
class_desc->pop();
|
|
for (int j = 0; j < cd.annotations[i].arguments.size(); j++) {
|
|
class_desc->push_color(text_color);
|
|
if (j > 0) {
|
|
class_desc->add_text(", ");
|
|
}
|
|
|
|
_add_text(cd.annotations[i].arguments[j].name);
|
|
class_desc->add_text(": ");
|
|
_add_type(cd.annotations[i].arguments[j].type);
|
|
if (!cd.annotations[i].arguments[j].default_value.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" = ");
|
|
class_desc->pop();
|
|
_add_text(cd.annotations[i].arguments[j].default_value);
|
|
}
|
|
|
|
class_desc->pop();
|
|
}
|
|
|
|
if (cd.annotations[i].qualifiers.contains("vararg")) {
|
|
class_desc->push_color(text_color);
|
|
if (cd.annotations[i].arguments.size()) {
|
|
class_desc->add_text(", ");
|
|
}
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("...");
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(")");
|
|
class_desc->pop();
|
|
}
|
|
|
|
if (!cd.annotations[i].qualifiers.is_empty()) {
|
|
class_desc->push_color(qualifier_color);
|
|
class_desc->add_text(" ");
|
|
_add_text(cd.annotations[i].qualifiers);
|
|
class_desc->pop();
|
|
}
|
|
|
|
class_desc->pop(); // end monofont
|
|
|
|
if (!cd.annotations[i].description.strip_edges().is_empty()) {
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_color(comment_color);
|
|
class_desc->push_indent(1);
|
|
_add_text(DTR(cd.annotations[i].description));
|
|
class_desc->pop(); // indent
|
|
class_desc->pop();
|
|
class_desc->pop(); // font
|
|
} else {
|
|
class_desc->push_indent(1);
|
|
class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
|
|
class_desc->add_text(" ");
|
|
class_desc->push_color(comment_color);
|
|
if (cd.is_script_doc) {
|
|
class_desc->append_text(TTR("There is currently no description for this annotation."));
|
|
} else {
|
|
class_desc->append_text(TTR("There is currently no description for this annotation. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
|
|
}
|
|
class_desc->pop();
|
|
class_desc->pop(); // indent
|
|
}
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
}
|
|
|
|
// Property descriptions
|
|
if (property_descr) {
|
|
section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Property Descriptions"));
|
|
class_desc->pop(); // font size
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
for (int i = 0; i < cd.properties.size(); i++) {
|
|
if (cd.properties[i].overridden) {
|
|
continue;
|
|
}
|
|
|
|
property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2;
|
|
|
|
class_desc->push_table(2);
|
|
class_desc->set_table_column_expand(1, true);
|
|
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
_add_bulletpoint();
|
|
|
|
_add_type(cd.properties[i].type, cd.properties[i].enumeration);
|
|
class_desc->add_text(" ");
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // cell
|
|
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_color(headline_color);
|
|
_add_text(cd.properties[i].name);
|
|
class_desc->pop(); // color
|
|
|
|
if (!cd.properties[i].default_value.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" [" + TTR("default:") + " ");
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->push_color(value_color);
|
|
_add_text(_fix_constant(cd.properties[i].default_value));
|
|
class_desc->pop(); // color
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("]");
|
|
class_desc->pop(); // color
|
|
}
|
|
|
|
if (cd.is_script_doc && (!cd.properties[i].setter.is_empty() || !cd.properties[i].getter.is_empty())) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(" [" + TTR("property:") + " ");
|
|
class_desc->pop(); // color
|
|
|
|
if (!cd.properties[i].setter.is_empty()) {
|
|
class_desc->push_color(value_color);
|
|
class_desc->add_text("setter");
|
|
class_desc->pop(); // color
|
|
}
|
|
if (!cd.properties[i].getter.is_empty()) {
|
|
if (!cd.properties[i].setter.is_empty()) {
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text(", ");
|
|
class_desc->pop(); // color
|
|
}
|
|
class_desc->push_color(value_color);
|
|
class_desc->add_text("getter");
|
|
class_desc->pop(); // color
|
|
}
|
|
|
|
class_desc->push_color(symbol_color);
|
|
class_desc->add_text("]");
|
|
class_desc->pop(); // color
|
|
}
|
|
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // cell
|
|
|
|
// Script doc doesn't have setter, getter.
|
|
if (!cd.is_script_doc) {
|
|
HashMap<String, DocData::MethodDoc> method_map;
|
|
for (int j = 0; j < methods.size(); j++) {
|
|
method_map[methods[j].name] = methods[j];
|
|
}
|
|
|
|
if (!cd.properties[i].setter.is_empty()) {
|
|
class_desc->push_cell();
|
|
class_desc->pop(); // cell
|
|
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_color(text_color);
|
|
if (method_map[cd.properties[i].setter].arguments.size() > 1) {
|
|
// Setters with additional arguments are exposed in the method list, so we link them here for quick access.
|
|
class_desc->push_meta("@method " + cd.properties[i].setter);
|
|
class_desc->add_text(cd.properties[i].setter + TTR("(value)"));
|
|
class_desc->pop();
|
|
} else {
|
|
class_desc->add_text(cd.properties[i].setter + TTR("(value)"));
|
|
}
|
|
class_desc->pop(); // color
|
|
class_desc->push_color(comment_color);
|
|
class_desc->add_text(" setter");
|
|
class_desc->pop(); // color
|
|
class_desc->pop(); // font
|
|
class_desc->pop(); // cell
|
|
method_line[cd.properties[i].setter] = property_line[cd.properties[i].name];
|
|
}
|
|
|
|
if (!cd.properties[i].getter.is_empty()) {
|
|
class_desc->push_cell();
|
|
class_desc->pop(); // cell
|
|
|
|
class_desc->push_cell();
|
|
class_desc->push_font(doc_code_font);
|
|
class_desc->push_color(text_color);
|
|
if (method_map[cd.properties[i].getter].arguments.size() > 0) {
|
|
// Getters with additional arguments are exposed in the method list, so we link them here for quick access.
|
|
class_desc->push_meta("@method " + cd.properties[i].getter);
|
|
class_desc->add_text(cd.properties[i].getter + "()");
|
|
class_desc->pop();
|
|
} else {
|
|
class_desc->add_text(cd.properties[i].getter + "()");
|
|
}
|
|
class_desc->pop(); //color
|
|
class_desc->push_color(comment_color);
|
|
class_desc->add_text(" getter");
|
|
class_desc->pop(); //color
|
|
class_desc->pop(); //font
|
|
class_desc->pop(); //cell
|
|
method_line[cd.properties[i].getter] = property_line[cd.properties[i].name];
|
|
}
|
|
}
|
|
|
|
class_desc->pop(); // table
|
|
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
|
|
class_desc->push_color(text_color);
|
|
class_desc->push_font(doc_font);
|
|
class_desc->push_indent(1);
|
|
if (!cd.properties[i].description.strip_edges().is_empty()) {
|
|
_add_text(DTR(cd.properties[i].description));
|
|
} else {
|
|
class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
|
|
class_desc->add_text(" ");
|
|
class_desc->push_color(comment_color);
|
|
if (cd.is_script_doc) {
|
|
class_desc->append_text(TTR("There is currently no description for this property."));
|
|
} else {
|
|
class_desc->append_text(TTR("There is currently no description for this property. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
|
|
}
|
|
class_desc->pop();
|
|
}
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->pop();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
class_desc->add_newline();
|
|
}
|
|
}
|
|
|
|
// Constructor descriptions
|
|
if (constructor_descriptions) {
|
|
section_line.push_back(Pair<String, int>(TTR("Constructor Descriptions"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Constructor Descriptions"));
|
|
_update_method_descriptions(cd, cd.constructors, "constructor");
|
|
}
|
|
|
|
// Method descriptions
|
|
if (method_descriptions) {
|
|
section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Method Descriptions"));
|
|
_update_method_descriptions(cd, methods, "method");
|
|
}
|
|
|
|
// Operator descriptions
|
|
if (operator_descriptions) {
|
|
section_line.push_back(Pair<String, int>(TTR("Operator Descriptions"), class_desc->get_paragraph_count() - 2));
|
|
class_desc->push_color(title_color);
|
|
class_desc->push_font(doc_title_font);
|
|
class_desc->push_font_size(doc_title_font_size);
|
|
class_desc->add_text(TTR("Operator Descriptions"));
|
|
_update_method_descriptions(cd, cd.operators, "operator");
|
|
}
|
|
scroll_locked = false;
|
|
}
|
|
|
|
void EditorHelp::_request_help(const String &p_string) {
|
|
Error err = _goto_desc(p_string);
|
|
if (err == OK) {
|
|
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
|
}
|
|
//100 palabras
|
|
}
|
|
|
|
void EditorHelp::_help_callback(const String &p_topic) {
|
|
String what = p_topic.get_slice(":", 0);
|
|
String clss = p_topic.get_slice(":", 1);
|
|
String name;
|
|
if (p_topic.get_slice_count(":") == 3) {
|
|
name = p_topic.get_slice(":", 2);
|
|
}
|
|
|
|
_request_help(clss); // First go to class.
|
|
|
|
int line = 0;
|
|
|
|
if (what == "class_desc") {
|
|
line = description_line;
|
|
} else if (what == "class_signal") {
|
|
if (signal_line.has(name)) {
|
|
line = signal_line[name];
|
|
}
|
|
} else if (what == "class_method" || what == "class_method_desc") {
|
|
if (method_line.has(name)) {
|
|
line = method_line[name];
|
|
}
|
|
} else if (what == "class_property") {
|
|
if (property_line.has(name)) {
|
|
line = property_line[name];
|
|
}
|
|
} else if (what == "class_enum") {
|
|
if (enum_line.has(name)) {
|
|
line = enum_line[name];
|
|
}
|
|
} else if (what == "class_theme_item") {
|
|
if (theme_property_line.has(name)) {
|
|
line = theme_property_line[name];
|
|
}
|
|
} else if (what == "class_constant") {
|
|
if (constant_line.has(name)) {
|
|
line = constant_line[name];
|
|
}
|
|
} else if (what == "class_annotation") {
|
|
if (annotation_line.has(name)) {
|
|
line = annotation_line[name];
|
|
}
|
|
} else if (what == "class_global") {
|
|
if (constant_line.has(name)) {
|
|
line = constant_line[name];
|
|
} else if (method_line.has(name)) {
|
|
line = method_line[name];
|
|
} else {
|
|
HashMap<String, HashMap<String, int>>::Iterator iter = enum_values_line.begin();
|
|
while (true) {
|
|
if (iter->value.has(name)) {
|
|
line = iter->value[name];
|
|
break;
|
|
} else if (iter == enum_values_line.last()) {
|
|
break;
|
|
} else {
|
|
++iter;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (class_desc->is_ready()) {
|
|
class_desc->call_deferred(SNAME("scroll_to_paragraph"), line);
|
|
} else {
|
|
scroll_to = line;
|
|
}
|
|
}
|
|
|
|
static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
|
|
DocTools *doc = EditorHelp::get_doc_data();
|
|
String base_path;
|
|
|
|
Ref<Font> doc_font = p_rt->get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
|
|
Ref<Font> doc_bold_font = p_rt->get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
|
|
Ref<Font> doc_italic_font = p_rt->get_theme_font(SNAME("doc_italic"), SNAME("EditorFonts"));
|
|
Ref<Font> doc_code_font = p_rt->get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
|
|
Ref<Font> doc_kbd_font = p_rt->get_theme_font(SNAME("doc_keyboard"), SNAME("EditorFonts"));
|
|
|
|
Color link_color = p_rt->get_theme_color(SNAME("link_color"), SNAME("EditorHelp"));
|
|
Color code_color = p_rt->get_theme_color(SNAME("code_color"), SNAME("EditorHelp"));
|
|
Color kbd_color = p_rt->get_theme_color(SNAME("kbd_color"), SNAME("EditorHelp"));
|
|
|
|
String bbcode = p_bbcode.dedent().replace("\t", "").replace("\r", "").strip_edges();
|
|
|
|
// Select the correct code examples.
|
|
switch ((int)EDITOR_GET("text_editor/help/class_reference_examples")) {
|
|
case 0: // GDScript
|
|
bbcode = bbcode.replace("[gdscript]", "[codeblock]");
|
|
bbcode = bbcode.replace("[/gdscript]", "[/codeblock]");
|
|
|
|
for (int pos = bbcode.find("[csharp]"); pos != -1; pos = bbcode.find("[csharp]")) {
|
|
int end_pos = bbcode.find("[/csharp]");
|
|
if (end_pos == -1) {
|
|
WARN_PRINT("Unclosed [csharp] block or parse fail in code (search for tag errors)");
|
|
break;
|
|
}
|
|
|
|
bbcode = bbcode.left(pos) + bbcode.substr(end_pos + 9); // 9 is length of "[/csharp]".
|
|
while (bbcode[pos] == '\n') {
|
|
bbcode = bbcode.left(pos) + bbcode.substr(pos + 1);
|
|
}
|
|
}
|
|
break;
|
|
case 1: // C#
|
|
bbcode = bbcode.replace("[csharp]", "[codeblock]");
|
|
bbcode = bbcode.replace("[/csharp]", "[/codeblock]");
|
|
|
|
for (int pos = bbcode.find("[gdscript]"); pos != -1; pos = bbcode.find("[gdscript]")) {
|
|
int end_pos = bbcode.find("[/gdscript]");
|
|
if (end_pos == -1) {
|
|
WARN_PRINT("Unclosed [gdscript] block or parse fail in code (search for tag errors)");
|
|
break;
|
|
}
|
|
|
|
bbcode = bbcode.left(pos) + bbcode.substr(end_pos + 11); // 11 is length of "[/gdscript]".
|
|
while (bbcode[pos] == '\n') {
|
|
bbcode = bbcode.left(pos) + bbcode.substr(pos + 1);
|
|
}
|
|
}
|
|
break;
|
|
case 2: // GDScript and C#
|
|
bbcode = bbcode.replace("[csharp]", "[b]C#:[/b]\n[codeblock]");
|
|
bbcode = bbcode.replace("[gdscript]", "[b]GDScript:[/b]\n[codeblock]");
|
|
|
|
bbcode = bbcode.replace("[/csharp]", "[/codeblock]");
|
|
bbcode = bbcode.replace("[/gdscript]", "[/codeblock]");
|
|
break;
|
|
}
|
|
|
|
// Remove codeblocks (they would be printed otherwise).
|
|
bbcode = bbcode.replace("[codeblocks]\n", "");
|
|
bbcode = bbcode.replace("\n[/codeblocks]", "");
|
|
bbcode = bbcode.replace("[codeblocks]", "");
|
|
bbcode = bbcode.replace("[/codeblocks]", "");
|
|
|
|
// Remove extra new lines around code blocks.
|
|
bbcode = bbcode.replace("[codeblock]\n", "[codeblock]");
|
|
bbcode = bbcode.replace("\n[/codeblock]", "[/codeblock]");
|
|
|
|
List<String> tag_stack;
|
|
bool code_tag = false;
|
|
bool codeblock_tag = false;
|
|
|
|
int pos = 0;
|
|
while (pos < bbcode.length()) {
|
|
int brk_pos = bbcode.find("[", pos);
|
|
|
|
if (brk_pos < 0) {
|
|
brk_pos = bbcode.length();
|
|
}
|
|
|
|
if (brk_pos > pos) {
|
|
String text = bbcode.substr(pos, brk_pos - pos);
|
|
if (!code_tag && !codeblock_tag) {
|
|
text = text.replace("\n", "\n\n");
|
|
}
|
|
p_rt->add_text(text);
|
|
}
|
|
|
|
if (brk_pos == bbcode.length()) {
|
|
break; // Nothing else to add.
|
|
}
|
|
|
|
int brk_end = bbcode.find("]", brk_pos + 1);
|
|
|
|
if (brk_end == -1) {
|
|
String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
|
|
if (!code_tag && !codeblock_tag) {
|
|
text = text.replace("\n", "\n\n");
|
|
}
|
|
p_rt->add_text(text);
|
|
|
|
break;
|
|
}
|
|
|
|
String tag = bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
|
|
|
|
if (tag.begins_with("/")) {
|
|
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
|
|
|
|
if (!tag_ok) {
|
|
p_rt->add_text("[");
|
|
pos = brk_pos + 1;
|
|
continue;
|
|
}
|
|
|
|
tag_stack.pop_front();
|
|
pos = brk_end + 1;
|
|
if (tag != "/img") {
|
|
p_rt->pop();
|
|
if (code_tag) {
|
|
// Pop both color and background color.
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
} else if (codeblock_tag) {
|
|
// Pop color, cell and table.
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
}
|
|
}
|
|
code_tag = false;
|
|
codeblock_tag = false;
|
|
|
|
} else if (code_tag || codeblock_tag) {
|
|
p_rt->add_text("[");
|
|
pos = brk_pos + 1;
|
|
|
|
} else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("annotation ") || tag.begins_with("theme_item ")) {
|
|
const int tag_end = tag.find(" ");
|
|
const String link_tag = tag.substr(0, tag_end);
|
|
const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
|
|
|
|
// Use monospace font with translucent colored background color to make clickable references
|
|
// easier to distinguish from inline code and other text.
|
|
p_rt->push_font(doc_code_font);
|
|
p_rt->push_color(link_color);
|
|
p_rt->push_bgcolor(code_color * Color(1, 1, 1, 0.15));
|
|
p_rt->push_meta("@" + link_tag + " " + link_target);
|
|
p_rt->add_text(link_target + (tag.begins_with("method ") ? "()" : ""));
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
pos = brk_end + 1;
|
|
|
|
} else if (tag.begins_with("param ")) {
|
|
const int tag_end = tag.find(" ");
|
|
const String param_name = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
|
|
|
|
// Use monospace font with translucent background color to make code easier to distinguish from other text.
|
|
p_rt->push_font(doc_code_font);
|
|
p_rt->push_bgcolor(Color(0.5, 0.5, 0.5, 0.15));
|
|
p_rt->push_color(code_color);
|
|
p_rt->add_text(param_name);
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
|
|
pos = brk_end + 1;
|
|
|
|
} else if (doc->class_list.has(tag)) {
|
|
// Class reference tag such as [Node2D] or [SceneTree].
|
|
// Use monospace font with translucent colored background color to make clickable references
|
|
// easier to distinguish from inline code and other text.
|
|
p_rt->push_font(doc_code_font);
|
|
p_rt->push_color(link_color);
|
|
p_rt->push_bgcolor(code_color * Color(1, 1, 1, 0.15));
|
|
p_rt->push_meta("#" + tag);
|
|
p_rt->add_text(tag);
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
p_rt->pop();
|
|
pos = brk_end + 1;
|
|
|
|
} else if (tag == "b") {
|
|
// Use bold font.
|
|
p_rt->push_font(doc_bold_font);
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "i") {
|
|
// Use italics font.
|
|
p_rt->push_font(doc_italic_font);
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "code") {
|
|
// Use monospace font with translucent background color to make code easier to distinguish from other text.
|
|
p_rt->push_font(doc_code_font);
|
|
p_rt->push_bgcolor(Color(0.5, 0.5, 0.5, 0.15));
|
|
p_rt->push_color(code_color);
|
|
code_tag = true;
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "codeblock") {
|
|
// Use monospace font with translucent background color to make code easier to distinguish from other text.
|
|
// Use a single-column table with cell row background color instead of `[bgcolor]`.
|
|
// This makes the background color highlight cover the entire block, rather than individual lines.
|
|
p_rt->push_font(doc_code_font);
|
|
p_rt->push_table(1);
|
|
p_rt->push_cell();
|
|
p_rt->set_cell_row_background_color(Color(0.5, 0.5, 0.5, 0.15), Color(0.5, 0.5, 0.5, 0.15));
|
|
p_rt->set_cell_padding(Rect2(10 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE));
|
|
p_rt->push_color(code_color);
|
|
codeblock_tag = true;
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "kbd") {
|
|
// Use keyboard font with custom color and background color.
|
|
p_rt->push_font(doc_kbd_font);
|
|
p_rt->push_bgcolor(Color(0.5, 0.5, 0.5, 0.15));
|
|
p_rt->push_color(kbd_color);
|
|
code_tag = true; // Though not strictly a code tag, logic is similar.
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "center") {
|
|
// Align to center.
|
|
p_rt->push_paragraph(HORIZONTAL_ALIGNMENT_CENTER, Control::TEXT_DIRECTION_AUTO, "");
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "br") {
|
|
// Force a line break.
|
|
p_rt->add_newline();
|
|
pos = brk_end + 1;
|
|
} else if (tag == "u") {
|
|
// Use underline.
|
|
p_rt->push_underline();
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag == "s") {
|
|
// Use strikethrough.
|
|
p_rt->push_strikethrough();
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
|
|
} else if (tag == "url") {
|
|
int end = bbcode.find("[", brk_end);
|
|
if (end == -1) {
|
|
end = bbcode.length();
|
|
}
|
|
String url = bbcode.substr(brk_end + 1, end - brk_end - 1);
|
|
p_rt->push_meta(url);
|
|
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag.begins_with("url=")) {
|
|
String url = tag.substr(4, tag.length());
|
|
p_rt->push_meta(url);
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front("url");
|
|
} else if (tag == "img") {
|
|
int end = bbcode.find("[", brk_end);
|
|
if (end == -1) {
|
|
end = bbcode.length();
|
|
}
|
|
String image = bbcode.substr(brk_end + 1, end - brk_end - 1);
|
|
|
|
Ref<Texture2D> texture = ResourceLoader::load(base_path.plus_file(image), "Texture2D");
|
|
if (texture.is_valid()) {
|
|
p_rt->add_image(texture);
|
|
}
|
|
|
|
pos = end;
|
|
tag_stack.push_front(tag);
|
|
} else if (tag.begins_with("color=")) {
|
|
String col = tag.substr(6, tag.length());
|
|
Color color = Color::from_string(col, Color());
|
|
p_rt->push_color(color);
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front("color");
|
|
|
|
} else if (tag.begins_with("font=")) {
|
|
String fnt = tag.substr(5, tag.length());
|
|
|
|
Ref<Font> font = ResourceLoader::load(base_path.plus_file(fnt), "Font");
|
|
if (font.is_valid()) {
|
|
p_rt->push_font(font);
|
|
} else {
|
|
p_rt->push_font(doc_font);
|
|
}
|
|
|
|
pos = brk_end + 1;
|
|
tag_stack.push_front("font");
|
|
|
|
} else {
|
|
p_rt->add_text("["); //ignore
|
|
pos = brk_pos + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_add_text(const String &p_bbcode) {
|
|
_add_text_to_rt(p_bbcode, class_desc);
|
|
}
|
|
|
|
Thread EditorHelp::thread;
|
|
|
|
void EditorHelp::_wait_for_thread() {
|
|
if (thread.is_started()) {
|
|
thread.wait_to_finish();
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_gen_doc_thread(void *p_udata) {
|
|
DocTools compdoc;
|
|
compdoc.load_compressed(_doc_data_compressed, _doc_data_compressed_size, _doc_data_uncompressed_size);
|
|
doc->merge_from(compdoc); // Ensure all is up to date.
|
|
}
|
|
|
|
static bool doc_gen_use_threads = true;
|
|
|
|
void EditorHelp::generate_doc() {
|
|
doc = memnew(DocTools);
|
|
// Not doable on threads unfortunately, since it instantiates all sorts of classes to get default values.
|
|
doc->generate(true);
|
|
|
|
if (doc_gen_use_threads) {
|
|
thread.start(_gen_doc_thread, nullptr);
|
|
} else {
|
|
_gen_doc_thread(nullptr);
|
|
}
|
|
}
|
|
|
|
void EditorHelp::_toggle_scripts_pressed() {
|
|
ScriptEditor::get_singleton()->toggle_scripts_panel();
|
|
update_toggle_scripts_button();
|
|
}
|
|
|
|
void EditorHelp::_notification(int p_what) {
|
|
switch (p_what) {
|
|
case NOTIFICATION_READY:
|
|
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
|
_wait_for_thread();
|
|
_update_doc();
|
|
} break;
|
|
|
|
case NOTIFICATION_THEME_CHANGED: {
|
|
if (is_inside_tree()) {
|
|
_class_desc_resized(true);
|
|
}
|
|
update_toggle_scripts_button();
|
|
} break;
|
|
|
|
case NOTIFICATION_VISIBILITY_CHANGED: {
|
|
update_toggle_scripts_button();
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void EditorHelp::go_to_help(const String &p_help) {
|
|
_wait_for_thread();
|
|
_help_callback(p_help);
|
|
}
|
|
|
|
void EditorHelp::go_to_class(const String &p_class, int p_scroll) {
|
|
_wait_for_thread();
|
|
_goto_desc(p_class, p_scroll);
|
|
}
|
|
|
|
void EditorHelp::update_doc() {
|
|
_wait_for_thread();
|
|
ERR_FAIL_COND(!doc->class_list.has(edited_class));
|
|
ERR_FAIL_COND(!doc->class_list[edited_class].is_script_doc);
|
|
_update_doc();
|
|
}
|
|
|
|
void EditorHelp::cleanup_doc() {
|
|
_wait_for_thread();
|
|
if (doc_gen_use_threads) {
|
|
thread.wait_to_finish();
|
|
}
|
|
memdelete(doc);
|
|
}
|
|
|
|
Vector<Pair<String, int>> EditorHelp::get_sections() {
|
|
_wait_for_thread();
|
|
Vector<Pair<String, int>> sections;
|
|
|
|
for (int i = 0; i < section_line.size(); i++) {
|
|
sections.push_back(Pair<String, int>(section_line[i].first, i));
|
|
}
|
|
return sections;
|
|
}
|
|
|
|
void EditorHelp::scroll_to_section(int p_section_index) {
|
|
_wait_for_thread();
|
|
int line = section_line[p_section_index].second;
|
|
if (class_desc->is_ready()) {
|
|
class_desc->scroll_to_paragraph(line);
|
|
} else {
|
|
scroll_to = line;
|
|
}
|
|
}
|
|
|
|
void EditorHelp::popup_search() {
|
|
_wait_for_thread();
|
|
find_bar->popup_search();
|
|
}
|
|
|
|
String EditorHelp::get_class() {
|
|
return edited_class;
|
|
}
|
|
|
|
void EditorHelp::search_again(bool p_search_previous) {
|
|
_search(p_search_previous);
|
|
}
|
|
|
|
int EditorHelp::get_scroll() const {
|
|
return class_desc->get_v_scroll_bar()->get_value();
|
|
}
|
|
|
|
void EditorHelp::set_scroll(int p_scroll) {
|
|
class_desc->get_v_scroll_bar()->set_value(p_scroll);
|
|
}
|
|
|
|
void EditorHelp::update_toggle_scripts_button() {
|
|
if (is_layout_rtl()) {
|
|
toggle_scripts_button->set_icon(get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"), SNAME("EditorIcons")));
|
|
} else {
|
|
toggle_scripts_button->set_icon(get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons")));
|
|
}
|
|
toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text()));
|
|
}
|
|
|
|
void EditorHelp::_bind_methods() {
|
|
ClassDB::bind_method("_class_list_select", &EditorHelp::_class_list_select);
|
|
ClassDB::bind_method("_request_help", &EditorHelp::_request_help);
|
|
ClassDB::bind_method("_search", &EditorHelp::_search);
|
|
ClassDB::bind_method("_help_callback", &EditorHelp::_help_callback);
|
|
|
|
ADD_SIGNAL(MethodInfo("go_to_help"));
|
|
}
|
|
|
|
EditorHelp::EditorHelp() {
|
|
set_custom_minimum_size(Size2(150 * EDSCALE, 0));
|
|
|
|
EDITOR_DEF("text_editor/help/sort_functions_alphabetically", true);
|
|
|
|
class_desc = memnew(RichTextLabel);
|
|
add_child(class_desc);
|
|
class_desc->set_threaded(true);
|
|
class_desc->set_v_size_flags(SIZE_EXPAND_FILL);
|
|
class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4));
|
|
|
|
class_desc->connect("finished", callable_mp(this, &EditorHelp::_class_desc_finished));
|
|
class_desc->connect("meta_clicked", callable_mp(this, &EditorHelp::_class_desc_select));
|
|
class_desc->connect("gui_input", callable_mp(this, &EditorHelp::_class_desc_input));
|
|
class_desc->connect("resized", callable_mp(this, &EditorHelp::_class_desc_resized).bind(false));
|
|
_class_desc_resized(false);
|
|
|
|
// Added second so it opens at the bottom so it won't offset the entire widget.
|
|
find_bar = memnew(FindBar);
|
|
add_child(find_bar);
|
|
find_bar->hide();
|
|
find_bar->set_rich_text_label(class_desc);
|
|
|
|
status_bar = memnew(HBoxContainer);
|
|
add_child(status_bar);
|
|
status_bar->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
status_bar->set_custom_minimum_size(Size2(0, 24 * EDSCALE));
|
|
|
|
toggle_scripts_button = memnew(Button);
|
|
toggle_scripts_button->set_flat(true);
|
|
toggle_scripts_button->connect("pressed", callable_mp(this, &EditorHelp::_toggle_scripts_pressed));
|
|
status_bar->add_child(toggle_scripts_button);
|
|
|
|
class_desc->set_selection_enabled(true);
|
|
|
|
class_desc->hide();
|
|
}
|
|
|
|
EditorHelp::~EditorHelp() {
|
|
}
|
|
|
|
DocTools *EditorHelp::get_doc_data() {
|
|
_wait_for_thread();
|
|
return doc;
|
|
}
|
|
|
|
//// EditorHelpBit ///
|
|
|
|
void EditorHelpBit::_go_to_help(String p_what) {
|
|
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
|
ScriptEditor::get_singleton()->goto_help(p_what);
|
|
emit_signal(SNAME("request_hide"));
|
|
}
|
|
|
|
void EditorHelpBit::_meta_clicked(String p_select) {
|
|
if (p_select.begins_with("$")) { //enum
|
|
|
|
String select = p_select.substr(1, p_select.length());
|
|
String class_name;
|
|
if (select.contains(".")) {
|
|
class_name = select.get_slice(".", 0);
|
|
} else {
|
|
class_name = "@Global";
|
|
}
|
|
_go_to_help("class_enum:" + class_name + ":" + select);
|
|
return;
|
|
} else if (p_select.begins_with("#")) {
|
|
_go_to_help("class_name:" + p_select.substr(1, p_select.length()));
|
|
return;
|
|
} else if (p_select.begins_with("@")) {
|
|
String m = p_select.substr(1, p_select.length());
|
|
|
|
if (m.contains(".")) {
|
|
_go_to_help("class_method:" + m.get_slice(".", 0) + ":" + m.get_slice(".", 0)); // Must go somewhere else.
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorHelpBit::_bind_methods() {
|
|
ClassDB::bind_method(D_METHOD("set_text", "text"), &EditorHelpBit::set_text);
|
|
ADD_SIGNAL(MethodInfo("request_hide"));
|
|
}
|
|
|
|
void EditorHelpBit::_notification(int p_what) {
|
|
switch (p_what) {
|
|
case NOTIFICATION_ENTER_TREE:
|
|
case NOTIFICATION_THEME_CHANGED: {
|
|
rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp")));
|
|
rich_text->clear();
|
|
_add_text_to_rt(text, rich_text);
|
|
rich_text->reset_size(); // Force recalculating size after parsing bbcode.
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void EditorHelpBit::set_text(const String &p_text) {
|
|
text = p_text;
|
|
rich_text->clear();
|
|
_add_text_to_rt(text, rich_text);
|
|
}
|
|
|
|
EditorHelpBit::EditorHelpBit() {
|
|
rich_text = memnew(RichTextLabel);
|
|
add_child(rich_text);
|
|
rich_text->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked));
|
|
rich_text->set_override_selected_font_color(false);
|
|
rich_text->set_fit_content_height(true);
|
|
set_custom_minimum_size(Size2(0, 50 * EDSCALE));
|
|
}
|
|
|
|
//// FindBar ///
|
|
|
|
FindBar::FindBar() {
|
|
search_text = memnew(LineEdit);
|
|
add_child(search_text);
|
|
search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
|
search_text->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
search_text->connect("text_changed", callable_mp(this, &FindBar::_search_text_changed));
|
|
search_text->connect("text_submitted", callable_mp(this, &FindBar::_search_text_submitted));
|
|
|
|
matches_label = memnew(Label);
|
|
add_child(matches_label);
|
|
matches_label->hide();
|
|
|
|
find_prev = memnew(Button);
|
|
find_prev->set_flat(true);
|
|
add_child(find_prev);
|
|
find_prev->set_focus_mode(FOCUS_NONE);
|
|
find_prev->connect("pressed", callable_mp(this, &FindBar::search_prev));
|
|
|
|
find_next = memnew(Button);
|
|
find_next->set_flat(true);
|
|
add_child(find_next);
|
|
find_next->set_focus_mode(FOCUS_NONE);
|
|
find_next->connect("pressed", callable_mp(this, &FindBar::search_next));
|
|
|
|
Control *space = memnew(Control);
|
|
add_child(space);
|
|
space->set_custom_minimum_size(Size2(4, 0) * EDSCALE);
|
|
|
|
hide_button = memnew(TextureButton);
|
|
add_child(hide_button);
|
|
hide_button->set_focus_mode(FOCUS_NONE);
|
|
hide_button->set_ignore_texture_size(true);
|
|
hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
|
|
hide_button->connect("pressed", callable_mp(this, &FindBar::_hide_bar));
|
|
}
|
|
|
|
void FindBar::popup_search() {
|
|
show();
|
|
bool grabbed_focus = false;
|
|
if (!search_text->has_focus()) {
|
|
search_text->grab_focus();
|
|
grabbed_focus = true;
|
|
}
|
|
|
|
if (!search_text->get_text().is_empty()) {
|
|
search_text->select_all();
|
|
search_text->set_caret_column(search_text->get_text().length());
|
|
if (grabbed_focus) {
|
|
_search();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FindBar::_notification(int p_what) {
|
|
switch (p_what) {
|
|
case NOTIFICATION_ENTER_TREE:
|
|
case NOTIFICATION_THEME_CHANGED: {
|
|
find_prev->set_icon(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")));
|
|
find_next->set_icon(get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons")));
|
|
hide_button->set_normal_texture(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
|
|
hide_button->set_hover_texture(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
|
|
hide_button->set_pressed_texture(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
|
|
hide_button->set_custom_minimum_size(hide_button->get_normal_texture()->get_size());
|
|
matches_label->add_theme_color_override("font_color", results_count > 0 ? get_theme_color(SNAME("font_color"), SNAME("Label")) : get_theme_color(SNAME("error_color"), SNAME("Editor")));
|
|
} break;
|
|
|
|
case NOTIFICATION_VISIBILITY_CHANGED: {
|
|
set_process_unhandled_input(is_visible_in_tree());
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void FindBar::_bind_methods() {
|
|
ADD_SIGNAL(MethodInfo("search"));
|
|
}
|
|
|
|
void FindBar::set_rich_text_label(RichTextLabel *p_rich_text_label) {
|
|
rich_text_label = p_rich_text_label;
|
|
}
|
|
|
|
bool FindBar::search_next() {
|
|
return _search();
|
|
}
|
|
|
|
bool FindBar::search_prev() {
|
|
return _search(true);
|
|
}
|
|
|
|
bool FindBar::_search(bool p_search_previous) {
|
|
String stext = search_text->get_text();
|
|
bool keep = prev_search == stext;
|
|
|
|
bool ret = rich_text_label->search(stext, keep, p_search_previous);
|
|
|
|
prev_search = stext;
|
|
|
|
if (ret) {
|
|
_update_results_count();
|
|
} else {
|
|
results_count = 0;
|
|
}
|
|
_update_matches_label();
|
|
|
|
return ret;
|
|
}
|
|
|
|
void FindBar::_update_results_count() {
|
|
results_count = 0;
|
|
|
|
String searched = search_text->get_text();
|
|
if (searched.is_empty()) {
|
|
return;
|
|
}
|
|
|
|
String full_text = rich_text_label->get_parsed_text();
|
|
|
|
int from_pos = 0;
|
|
|
|
while (true) {
|
|
int pos = full_text.findn(searched, from_pos);
|
|
if (pos == -1) {
|
|
break;
|
|
}
|
|
|
|
results_count++;
|
|
from_pos = pos + searched.length();
|
|
}
|
|
}
|
|
|
|
void FindBar::_update_matches_label() {
|
|
if (search_text->get_text().is_empty() || results_count == -1) {
|
|
matches_label->hide();
|
|
} else {
|
|
matches_label->show();
|
|
|
|
matches_label->add_theme_color_override("font_color", results_count > 0 ? get_theme_color(SNAME("font_color"), SNAME("Label")) : get_theme_color(SNAME("error_color"), SNAME("Editor")));
|
|
matches_label->set_text(vformat(results_count == 1 ? TTR("%d match.") : TTR("%d matches."), results_count));
|
|
}
|
|
}
|
|
|
|
void FindBar::_hide_bar() {
|
|
if (search_text->has_focus()) {
|
|
rich_text_label->grab_focus();
|
|
}
|
|
|
|
hide();
|
|
}
|
|
|
|
void FindBar::unhandled_input(const Ref<InputEvent> &p_event) {
|
|
ERR_FAIL_COND(p_event.is_null());
|
|
|
|
Ref<InputEventKey> k = p_event;
|
|
if (k.is_valid()) {
|
|
if (k->is_pressed() && (rich_text_label->has_focus() || is_ancestor_of(get_viewport()->gui_get_focus_owner()))) {
|
|
bool accepted = true;
|
|
|
|
switch (k->get_keycode()) {
|
|
case Key::ESCAPE: {
|
|
_hide_bar();
|
|
} break;
|
|
default: {
|
|
accepted = false;
|
|
} break;
|
|
}
|
|
|
|
if (accepted) {
|
|
accept_event();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FindBar::_search_text_changed(const String &p_text) {
|
|
search_next();
|
|
}
|
|
|
|
void FindBar::_search_text_submitted(const String &p_text) {
|
|
if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
|
|
search_prev();
|
|
} else {
|
|
search_next();
|
|
}
|
|
}
|