2014-02-09 22:10:30 -03:00
/**************************************************************************/
/* editor_help.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-09 22:10:30 -03:00
# include "editor_help.h"
2017-01-16 08:04:19 +01:00
2024-04-11 11:21:44 +03:00
# include "core/config/project_settings.h"
2021-08-24 15:16:25 -03:00
# include "core/core_constants.h"
2023-10-19 00:50:30 +02:00
# include "core/extension/gdextension.h"
2020-04-28 15:19:37 +02:00
# include "core/input/input.h"
2024-12-14 21:25:05 +03:00
# include "core/io/json.h"
2023-04-21 09:32:26 -04:00
# include "core/object/script_language.h"
2018-09-11 18:13:45 +02:00
# include "core/os/keyboard.h"
2024-01-17 15:22:43 +01:00
# include "core/string/string_builder.h"
2025-02-20 08:40:46 -06:00
# include "core/version.h"
2024-01-15 13:14:55 +01:00
# include "editor/doc_data_compressed.gen.h"
2024-08-19 17:03:03 -04:00
# include "editor/editor_file_system.h"
2024-08-19 18:08:31 -04:00
# include "editor/editor_main_screen.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_node.h"
2023-02-07 21:14:00 +01:00
# include "editor/editor_paths.h"
2023-09-20 23:54:51 -03:00
# include "editor/editor_property_name_processor.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_settings.h"
2023-08-13 02:33:39 +02:00
# include "editor/editor_string_names.h"
2024-12-25 03:52:07 +01:00
# include "editor/filesystem_dock.h"
2024-11-20 00:34:04 +01:00
# include "editor/gui/editor_toaster.h"
2017-03-05 14:21:25 +01:00
# include "editor/plugins/script_editor_plugin.h"
2024-01-15 13:14:55 +01:00
# include "editor/themes/editor_scale.h"
2022-11-19 12:45:49 +01:00
# include "scene/gui/line_edit.h"
2014-02-20 23:01:44 -03:00
2024-03-08 22:16:55 +03:00
# include "modules/modules_enabled.gen.h" // For gdscript, mono.
// For syntax highlighting.
# ifdef MODULE_GDSCRIPT_ENABLED
# include "modules/gdscript/editor/gdscript_highlighter.h"
# include "modules/gdscript/gdscript.h"
# endif
// For syntax highlighting.
# ifdef MODULE_MONO_ENABLED
# include "editor/plugins/script_editor_plugin.h"
# include "modules/mono/csharp_script.h"
# endif
2025-03-03 22:27:29 -08:00
# define CONTRIBUTE_URL vformat("%s / contributing / documentation / updating_the_class_reference.html", GODOT_VERSION_DOCS_URL)
2017-09-12 19:16:18 -03:00
2023-07-08 13:17:41 +02:00
# ifdef MODULE_MONO_ENABLED
// Sync with the types mentioned in https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_differences.html
const Vector < String > classes_with_csharp_differences = {
" @GlobalScope " ,
" String " ,
" NodePath " ,
" Signal " ,
" Callable " ,
" RID " ,
" Basis " ,
" Transform2D " ,
" Transform3D " ,
" Rect2 " ,
" Rect2i " ,
" AABB " ,
" Quaternion " ,
" Projection " ,
" Color " ,
" Array " ,
" Dictionary " ,
" PackedByteArray " ,
" PackedColorArray " ,
" PackedFloat32Array " ,
" PackedFloat64Array " ,
" PackedInt32Array " ,
" PackedInt64Array " ,
" PackedStringArray " ,
" PackedVector2Array " ,
" PackedVector3Array " ,
2024-04-08 07:51:34 -07:00
" PackedVector4Array " ,
2023-07-08 13:17:41 +02:00
" Variant " ,
} ;
# endif
2024-05-28 12:15:00 +02:00
static const char32_t nbsp_chr = 160 ;
static const String nbsp = String : : chr ( nbsp_chr ) ;
2024-12-14 21:25:05 +03:00
static const String nbsp_equal_nbsp = nbsp + " = " + nbsp ;
static const String colon_nbsp = " : " + nbsp ;
2024-01-23 16:39:26 +01:00
const Vector < String > packed_array_types = {
" PackedByteArray " ,
" PackedColorArray " ,
" PackedFloat32Array " ,
" PackedFloat64Array " ,
" PackedInt32Array " ,
" PackedInt64Array " ,
" PackedStringArray " ,
" PackedVector2Array " ,
" PackedVector3Array " ,
2024-04-08 07:51:34 -07:00
" PackedVector4Array " ,
2024-01-23 16:39:26 +01:00
} ;
2024-12-14 21:25:05 +03:00
static String _replace_nbsp_with_space ( const String & p_string ) {
2024-05-28 12:15:00 +02:00
return p_string . replace_char ( nbsp_chr , ' ' ) ;
2024-12-14 21:25:05 +03:00
}
2014-02-15 21:16:33 -03:00
2024-12-14 21:25:05 +03:00
static String _fix_constant ( const String & p_constant ) {
if ( p_constant . strip_edges ( ) = = " 4294967295 " ) {
return " 0xFFFFFFFF " ;
2023-04-21 09:32:26 -04:00
}
2024-12-14 21:25:05 +03:00
if ( p_constant . strip_edges ( ) = = " 2147483647 " ) {
return " 0x7FFFFFFF " ;
2023-04-21 09:32:26 -04:00
}
2024-12-14 21:25:05 +03:00
if ( p_constant . strip_edges ( ) = = " 1048575 " ) {
return " 0xFFFFF " ;
}
return p_constant ;
2023-04-21 09:32:26 -04:00
}
2024-12-14 21:25:05 +03:00
static void _add_qualifiers_to_rt ( const String & p_qualifiers , RichTextLabel * p_rt ) {
for ( const String & qualifier : p_qualifiers . split_spaces ( ) ) {
String hint ;
if ( qualifier = = " vararg " ) {
hint = TTR ( " This method supports a variable number of arguments. " ) ;
} else if ( qualifier = = " virtual " ) {
hint = TTR ( " This method is called by the engine. \n It can be overridden to customize built-in behavior. " ) ;
2025-06-05 11:11:08 +08:00
} else if ( qualifier = = " required " ) {
hint = TTR ( " This method is required to be overridden when extending its base class. " ) ;
2024-12-14 21:25:05 +03:00
} else if ( qualifier = = " const " ) {
hint = TTR ( " This method has no side effects. \n It does not modify the object in any way. " ) ;
} else if ( qualifier = = " static " ) {
hint = TTR ( " This method does not need an instance to be called. \n It can be called directly using the class name. " ) ;
2025-05-14 22:52:19 +03:00
} else if ( qualifier = = " abstract " ) {
hint = TTR ( " This method must be implemented to complete the abstract class. " ) ;
2024-12-14 21:25:05 +03:00
}
p_rt - > add_text ( " " ) ;
if ( hint . is_empty ( ) ) {
p_rt - > add_text ( qualifier ) ;
} else {
p_rt - > push_hint ( hint ) ;
p_rt - > add_text ( qualifier ) ;
p_rt - > pop ( ) ; // hint
}
}
}
// Removes unnecessary prefix from `p_class_specifier` when within the `p_edited_class` context.
2023-04-21 09:32:26 -04:00
static String _contextualize_class_specifier ( const String & p_class_specifier , const String & p_edited_class ) {
2024-12-14 21:25:05 +03:00
// If this is a completely different context than the current class, then keep full path.
2023-04-21 09:32:26 -04:00
if ( ! p_class_specifier . begins_with ( p_edited_class ) ) {
return p_class_specifier ;
}
2024-12-14 21:25:05 +03:00
// Here equal `length()` and `begins_with()` from above implies `p_class_specifier == p_edited_class`.
2023-04-21 09:32:26 -04:00
if ( p_class_specifier . length ( ) = = p_edited_class . length ( ) ) {
2024-11-16 18:52:15 +01:00
int rfind = p_class_specifier . rfind_char ( ' . ' ) ;
2024-12-14 21:25:05 +03:00
if ( rfind = = - 1 ) { // Single identifier.
2023-04-21 09:32:26 -04:00
return p_class_specifier ;
}
2024-12-14 21:25:05 +03:00
// Multiple specifiers: keep last one only.
2023-04-21 09:32:26 -04:00
return p_class_specifier . substr ( rfind + 1 ) ;
}
2024-12-14 21:25:05 +03:00
// They share a _name_ prefix but not a _class specifier_ prefix, e.g. `Tree` and `TreeItem`.
// `begins_with()` and `length()`s being different implies `p_class_specifier.length() > p_edited_class.length()` so this is safe.
2023-04-28 06:49:24 -04:00
if ( p_class_specifier [ p_edited_class . length ( ) ] ! = ' . ' ) {
return p_class_specifier ;
}
2024-12-14 21:25:05 +03:00
// Remove class specifier prefix.
2023-04-21 09:32:26 -04:00
return p_class_specifier . substr ( p_edited_class . length ( ) + 1 ) ;
}
2024-12-14 21:25:05 +03:00
/// EditorHelp ///
2023-02-11 16:29:44 +01:00
void EditorHelp : : _update_theme_item_cache ( ) {
VBoxContainer : : _update_theme_item_cache ( ) ;
theme_cache . text_color = get_theme_color ( SNAME ( " text_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . title_color = get_theme_color ( SNAME ( " title_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . headline_color = get_theme_color ( SNAME ( " headline_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . comment_color = get_theme_color ( SNAME ( " comment_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . symbol_color = get_theme_color ( SNAME ( " symbol_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . value_color = get_theme_color ( SNAME ( " value_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . qualifier_color = get_theme_color ( SNAME ( " qualifier_color " ) , SNAME ( " EditorHelp " ) ) ;
theme_cache . type_color = get_theme_color ( SNAME ( " type_color " ) , SNAME ( " EditorHelp " ) ) ;
2024-01-16 16:54:45 +01:00
theme_cache . override_color = get_theme_color ( SNAME ( " override_color " ) , SNAME ( " EditorHelp " ) ) ;
2023-02-11 16:29:44 +01:00
2023-08-13 02:33:39 +02:00
theme_cache . doc_font = get_theme_font ( SNAME ( " doc " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_bold_font = get_theme_font ( SNAME ( " doc_bold " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_italic_font = get_theme_font ( SNAME ( " doc_italic " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_title_font = get_theme_font ( SNAME ( " doc_title " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_code_font = get_theme_font ( SNAME ( " doc_source " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_kbd_font = get_theme_font ( SNAME ( " doc_keyboard " ) , EditorStringName ( EditorFonts ) ) ;
2023-02-11 16:29:44 +01:00
2023-08-13 02:33:39 +02:00
theme_cache . doc_font_size = get_theme_font_size ( SNAME ( " doc_size " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_title_font_size = get_theme_font_size ( SNAME ( " doc_title_size " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_code_font_size = get_theme_font_size ( SNAME ( " doc_source_size " ) , EditorStringName ( EditorFonts ) ) ;
theme_cache . doc_kbd_font_size = get_theme_font_size ( SNAME ( " doc_keyboard_size " ) , EditorStringName ( EditorFonts ) ) ;
2023-02-11 16:29:44 +01:00
theme_cache . background_style = get_theme_stylebox ( SNAME ( " background " ) , SNAME ( " EditorHelp " ) ) ;
2022-08-29 11:04:31 +02:00
2023-10-19 18:05:19 +02:00
class_desc - > begin_bulk_theme_override ( ) ;
2023-02-11 17:33:38 +01:00
class_desc - > add_theme_font_override ( " normal_font " , theme_cache . doc_font ) ;
class_desc - > add_theme_font_size_override ( " normal_font_size " , theme_cache . doc_font_size ) ;
2024-05-13 16:56:03 +02:00
class_desc - > add_theme_constant_override ( SceneStringName ( line_separation ) , get_theme_constant ( SceneStringName ( line_separation ) , SNAME ( " EditorHelp " ) ) ) ;
2022-08-29 11:04:31 +02:00
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 " ) ) ) ;
2022-10-31 19:51:48 +01:00
class_desc - > add_theme_constant_override ( " text_highlight_h_padding " , get_theme_constant ( SNAME ( " text_highlight_h_padding " ) , SNAME ( " EditorHelp " ) ) ) ;
class_desc - > add_theme_constant_override ( " text_highlight_v_padding " , get_theme_constant ( SNAME ( " text_highlight_v_padding " ) , SNAME ( " EditorHelp " ) ) ) ;
2023-10-19 18:05:19 +02:00
class_desc - > end_bulk_theme_override ( ) ;
2022-08-29 11:04:31 +02:00
}
2019-10-09 17:41:49 +02:00
void EditorHelp : : _search ( bool p_search_previous ) {
2020-05-14 16:41:43 +02:00
if ( p_search_previous ) {
2019-10-09 17:41:49 +02:00
find_bar - > search_prev ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2019-10-09 17:41:49 +02:00
find_bar - > search_next ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
}
2022-05-30 10:04:07 +03:00
void EditorHelp : : _class_desc_finished ( ) {
if ( scroll_to > = 0 ) {
2024-12-07 18:24:21 -03:00
class_desc - > connect ( SceneStringName ( draw ) , callable_mp ( class_desc , & RichTextLabel : : scroll_to_paragraph ) . bind ( scroll_to ) , CONNECT_ONE_SHOT | CONNECT_DEFERRED ) ;
2022-05-30 10:04:07 +03:00
}
scroll_to = - 1 ;
}
2014-02-09 22:10:30 -03:00
void EditorHelp : : _class_list_select ( const String & p_select ) {
_goto_desc ( p_select ) ;
}
void EditorHelp : : _class_desc_select ( const String & p_select ) {
2024-04-11 11:21:44 +03:00
if ( p_select . begins_with ( " $ " ) ) { // Enum.
const String link = p_select . substr ( 1 ) ;
String enum_class_name ;
String enum_name ;
if ( CoreConstants : : is_global_enum ( link ) ) {
enum_class_name = " @GlobalScope " ;
enum_name = link ;
2017-08-23 19:10:32 -03:00
} else {
2024-11-16 18:52:15 +01:00
const int dot_pos = link . rfind_char ( ' . ' ) ;
2024-04-11 11:21:44 +03:00
if ( dot_pos > = 0 ) {
enum_class_name = link . left ( dot_pos ) ;
enum_name = link . substr ( dot_pos + 1 ) ;
} else {
enum_class_name = edited_class ;
enum_name = link ;
}
2017-08-23 19:10:32 -03:00
}
2019-04-27 21:15:07 +02:00
2024-04-11 11:21:44 +03:00
emit_signal ( SNAME ( " go_to_help " ) , " class_enum: " + enum_class_name + " : " + enum_name ) ;
} else if ( p_select . begins_with ( " # " ) ) { // Class.
emit_signal ( SNAME ( " go_to_help " ) , " class_name: " + p_select . substr ( 1 ) ) ;
} else if ( p_select . begins_with ( " @ " ) ) { // Member.
const int tag_end = p_select . find_char ( ' ' ) ;
const String tag = p_select . substr ( 1 , tag_end - 1 ) ;
const String link = p_select . substr ( tag_end + 1 ) . lstrip ( " " ) ;
2017-11-20 21:30:46 -02:00
String topic ;
2024-04-11 11:21:44 +03:00
const HashMap < String , int > * table = nullptr ;
2017-11-20 21:30:46 -02:00
if ( tag = = " method " ) {
topic = " class_method " ;
2024-01-28 21:51:39 +01:00
table = & method_line ;
2023-10-02 20:11:43 +02:00
} else if ( tag = = " constructor " ) {
topic = " class_method " ;
2024-01-28 21:51:39 +01:00
table = & method_line ;
2023-10-02 20:11:43 +02:00
} else if ( tag = = " operator " ) {
topic = " class_method " ;
2024-01-28 21:51:39 +01:00
table = & method_line ;
2017-11-20 21:30:46 -02:00
} else if ( tag = = " member " ) {
topic = " class_property " ;
2024-01-28 21:51:39 +01:00
table = & property_line ;
2019-03-27 20:01:16 +01:00
} else if ( tag = = " enum " ) {
2017-11-20 21:30:46 -02:00
topic = " class_enum " ;
2024-01-28 21:51:39 +01:00
table = & enum_line ;
2017-11-20 21:30:46 -02:00
} else if ( tag = = " signal " ) {
topic = " class_signal " ;
2024-01-28 21:51:39 +01:00
table = & signal_line ;
2019-03-27 20:01:16 +01:00
} else if ( tag = = " constant " ) {
topic = " class_constant " ;
2024-01-28 21:51:39 +01:00
table = & constant_line ;
2022-07-04 18:56:34 +03:00
} else if ( tag = = " annotation " ) {
topic = " class_annotation " ;
2024-01-28 21:51:39 +01:00
table = & annotation_line ;
2021-11-18 15:03:03 +01:00
} else if ( tag = = " theme_item " ) {
2024-02-12 16:55:02 +03:00
topic = " class_theme_item " ;
2024-01-28 21:51:39 +01:00
table = & theme_property_line ;
2017-11-20 21:30:46 -02:00
} else {
return ;
}
2014-02-09 22:10:30 -03:00
2024-12-14 21:25:05 +03:00
// Case order is important here to correctly handle edge cases like `Variant.Type` in `@GlobalScope`.
2022-04-26 17:57:36 +01:00
if ( table - > has ( link ) ) {
// Found in the current page.
2024-07-04 16:55:51 +02:00
if ( class_desc - > is_finished ( ) ) {
2022-10-22 23:54:37 +02:00
emit_signal ( SNAME ( " request_save_history " ) ) ;
2022-05-30 10:04:07 +03:00
class_desc - > scroll_to_paragraph ( ( * table ) [ link ] ) ;
} else {
scroll_to = ( * table ) [ link ] ;
}
2022-05-20 23:11:48 +01:00
} else {
2024-12-14 21:25:05 +03:00
// Look for link in `@GlobalScope`.
2022-05-20 23:11:48 +01:00
if ( topic = = " class_enum " ) {
const DocData : : ClassDoc & cd = doc - > class_list [ " @GlobalScope " ] ;
2024-12-14 21:25:05 +03:00
const String enum_link = link . trim_prefix ( " @GlobalScope. " ) ;
2022-05-20 23:11:48 +01:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : ConstantDoc & constant : cd . constants ) {
2024-12-14 21:25:05 +03:00
if ( constant . enumeration = = enum_link ) {
// Found in `@GlobalScope`.
emit_signal ( SNAME ( " go_to_help " ) , topic + " :@GlobalScope: " + enum_link ) ;
2022-05-20 23:11:48 +01:00
return ;
}
2019-03-27 20:01:16 +01:00
}
2022-05-20 23:11:48 +01:00
} else if ( topic = = " class_constant " ) {
const DocData : : ClassDoc & cd = doc - > class_list [ " @GlobalScope " ] ;
2024-02-16 16:28:05 +03:00
for ( const DocData : : ConstantDoc & constant : cd . constants ) {
if ( constant . name = = link ) {
2024-12-14 21:25:05 +03:00
// Found in `@GlobalScope`.
2022-05-20 23:11:48 +01:00
emit_signal ( SNAME ( " go_to_help " ) , topic + " :@GlobalScope: " + link ) ;
return ;
}
2022-04-26 17:57:36 +01:00
}
}
2022-05-20 23:11:48 +01:00
2024-12-05 17:56:08 +01:00
if ( link . contains_char ( ' . ' ) ) {
2024-12-14 21:25:05 +03:00
const int class_end = link . rfind_char ( ' . ' ) ;
2024-04-11 11:21:44 +03:00
emit_signal ( SNAME ( " go_to_help " ) , topic + " : " + link . left ( class_end ) + " : " + link . substr ( class_end + 1 ) ) ;
2022-05-20 23:11:48 +01:00
}
2015-11-17 09:46:08 -03:00
}
2024-04-11 11:21:44 +03:00
} else if ( p_select . begins_with ( " http: " ) | | p_select . begins_with ( " https: " ) ) {
2017-09-12 19:16:18 -03:00
OS : : get_singleton ( ) - > shell_open ( p_select ) ;
2024-04-11 11:21:44 +03:00
} else if ( p_select . begins_with ( " ^ " ) ) { // Copy button.
DisplayServer : : get_singleton ( ) - > clipboard_set ( p_select . substr ( 1 ) ) ;
2024-11-20 00:34:04 +01:00
EditorToaster : : get_singleton ( ) - > popup_str ( TTR ( " Code snippet copied to clipboard. " ) , EditorToaster : : SEVERITY_INFO ) ;
2014-02-09 22:10:30 -03:00
}
}
2017-05-20 12:38:03 -03:00
void EditorHelp : : _class_desc_input ( const Ref < InputEvent > & p_input ) {
2016-06-04 03:11:34 +09:00
}
2022-01-29 20:48:30 +03:00
void EditorHelp : : _class_desc_resized ( bool p_force_update_theme ) {
2019-06-05 20:47:34 +02:00
// Add extra horizontal margins for better readability.
// The margins increase as the width of the editor help container increases.
2023-02-11 16:29:44 +01:00
real_t char_width = theme_cache . doc_code_font - > get_char_size ( ' x ' , theme_cache . doc_code_font_size ) . width ;
2022-01-24 09:51:35 +02:00
const int new_display_margin = MAX ( 30 * EDSCALE , get_parent_anchorable_rect ( ) . size . width - char_width * 120 * EDSCALE ) * 0.5 ;
2022-01-29 20:48:30 +03:00
if ( display_margin ! = new_display_margin | | p_force_update_theme ) {
2022-01-24 09:51:35 +02:00
display_margin = new_display_margin ;
2023-02-11 16:29:44 +01:00
Ref < StyleBox > class_desc_stylebox = theme_cache . background_style - > duplicate ( ) ;
2023-01-19 19:14:09 +03:00
class_desc_stylebox - > set_content_margin ( SIDE_LEFT , display_margin ) ;
class_desc_stylebox - > set_content_margin ( SIDE_RIGHT , display_margin ) ;
2024-05-14 14:41:39 +02:00
class_desc - > add_theme_style_override ( CoreStringName ( normal ) , class_desc_stylebox ) ;
2022-12-09 18:54:52 +01:00
class_desc - > add_theme_style_override ( " focused " , class_desc_stylebox ) ;
2022-01-24 09:51:35 +02:00
}
2019-06-05 20:47:34 +02:00
}
2024-04-11 11:21:44 +03:00
static void _add_type_to_rt ( const String & p_type , const String & p_enum , bool p_is_bitfield , RichTextLabel * p_rt , const Control * p_owner_node , const String & p_class ) {
const Color type_color = p_owner_node - > get_theme_color ( SNAME ( " type_color " ) , SNAME ( " EditorHelp " ) ) ;
2022-11-01 23:45:40 +01:00
if ( p_type . is_empty ( ) | | p_type = = " void " ) {
2024-04-11 11:21:44 +03:00
p_rt - > push_color ( Color ( type_color , 0.5 ) ) ;
p_rt - > push_hint ( TTR ( " No return value. " ) ) ;
p_rt - > add_text ( " void " ) ;
p_rt - > pop ( ) ; // hint
p_rt - > pop ( ) ; // color
2022-11-01 23:45:40 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2022-11-01 23:45:40 +01:00
bool is_enum_type = ! p_enum . is_empty ( ) ;
2023-06-15 17:06:22 +03:00
bool is_bitfield = p_is_bitfield & & is_enum_type ;
2024-12-05 17:56:08 +01:00
bool can_ref = ! p_type . contains_char ( ' * ' ) | | is_enum_type ;
2022-11-01 23:45:40 +01:00
2023-04-21 09:32:26 -04:00
String link_t = p_type ; // For links in metadata
2023-06-09 17:00:50 +02:00
String display_t ; // For display purposes.
2022-11-01 23:45:40 +01:00
if ( is_enum_type ) {
2023-04-21 09:32:26 -04:00
link_t = p_enum ; // The link for enums is always the full enum description
2024-04-11 11:21:44 +03:00
display_t = _contextualize_class_specifier ( p_enum , p_class ) ;
2023-04-21 09:32:26 -04:00
} else {
2024-04-11 11:21:44 +03:00
display_t = _contextualize_class_specifier ( p_type , p_class ) ;
2017-08-23 19:10:32 -03:00
}
2022-01-18 23:01:30 +03:00
2024-04-11 11:21:44 +03:00
p_rt - > push_color ( type_color ) ;
2023-06-24 13:03:28 -05:00
bool add_typed_container = false ;
2017-08-23 19:10:32 -03:00
if ( can_ref ) {
2023-04-21 09:32:26 -04:00
if ( link_t . ends_with ( " [] " ) ) {
2023-06-24 13:03:28 -05:00
add_typed_container = true ;
2023-05-13 16:09:35 +03:00
link_t = link_t . trim_suffix ( " [] " ) ;
display_t = display_t . trim_suffix ( " [] " ) ;
2022-11-01 23:45:40 +01:00
2024-04-11 11:21:44 +03:00
p_rt - > push_meta ( " #Array " , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ; // class
p_rt - > add_text ( " Array " ) ;
p_rt - > pop ( ) ; // meta
p_rt - > add_text ( " [ " ) ;
2023-06-24 13:03:28 -05:00
} else if ( link_t . begins_with ( " Dictionary[ " ) ) {
add_typed_container = true ;
link_t = link_t . trim_prefix ( " Dictionary[ " ) . trim_suffix ( " ] " ) ;
display_t = display_t . trim_prefix ( " Dictionary[ " ) . trim_suffix ( " ] " ) ;
p_rt - > push_meta ( " #Dictionary " , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ; // class
p_rt - > add_text ( " Dictionary " ) ;
p_rt - > pop ( ) ; // meta
p_rt - > add_text ( " [ " ) ;
p_rt - > push_meta ( " # " + link_t . get_slice ( " , " , 0 ) , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ; // class
p_rt - > add_text ( _contextualize_class_specifier ( display_t . get_slice ( " , " , 0 ) , p_class ) ) ;
p_rt - > pop ( ) ; // meta
p_rt - > add_text ( " , " ) ;
link_t = link_t . get_slice ( " , " , 1 ) ;
display_t = _contextualize_class_specifier ( display_t . get_slice ( " , " , 1 ) , p_class ) ;
2023-06-15 17:06:22 +03:00
} else if ( is_bitfield ) {
2024-04-11 11:21:44 +03:00
p_rt - > push_color ( Color ( type_color , 0.5 ) ) ;
p_rt - > push_hint ( TTR ( " This value is an integer composed as a bitmask of the following flags. " ) ) ;
p_rt - > add_text ( " BitField " ) ;
p_rt - > pop ( ) ; // hint
p_rt - > add_text ( " [ " ) ;
p_rt - > pop ( ) ; // color
2020-04-20 19:06:00 -03:00
}
2022-11-01 23:45:40 +01:00
if ( is_enum_type ) {
2024-04-11 11:21:44 +03:00
p_rt - > push_meta ( " $ " + link_t , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ; // enum
2022-11-01 23:45:40 +01:00
} else {
2024-04-11 11:21:44 +03:00
p_rt - > push_meta ( " # " + link_t , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ; // class
2017-08-23 19:10:32 -03:00
}
}
2024-04-11 11:21:44 +03:00
p_rt - > add_text ( display_t ) ;
2020-04-20 19:06:00 -03:00
if ( can_ref ) {
2024-04-11 11:21:44 +03:00
p_rt - > pop ( ) ; // meta
2023-06-24 13:03:28 -05:00
if ( add_typed_container ) {
2024-04-11 11:21:44 +03:00
p_rt - > add_text ( " ] " ) ;
2023-06-15 17:06:22 +03:00
} else if ( is_bitfield ) {
2024-04-11 11:21:44 +03:00
p_rt - > push_color ( Color ( type_color , 0.5 ) ) ;
p_rt - > add_text ( " ] " ) ;
p_rt - > pop ( ) ; // color
2020-04-20 19:06:00 -03:00
}
}
2024-04-11 11:21:44 +03:00
p_rt - > pop ( ) ; // color
}
void EditorHelp : : _add_type ( const String & p_type , const String & p_enum , bool p_is_bitfield ) {
_add_type_to_rt ( p_type , p_enum , p_is_bitfield , class_desc , this , edited_class ) ;
2014-02-09 22:10:30 -03:00
}
2023-03-31 21:17:59 +02:00
void EditorHelp : : _add_type_icon ( const String & p_type , int p_size , const String & p_fallback ) {
Ref < Texture2D > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( p_type , p_fallback ) ;
2022-08-24 20:41:31 +02:00
Vector2i size = Vector2i ( icon - > get_width ( ) , icon - > get_height ( ) ) ;
if ( p_size > 0 ) {
2023-08-01 20:04:28 +03:00
// Ensures icon scales proportionally on both axes, based on icon height.
2022-08-24 20:41:31 +02:00
float ratio = p_size / float ( size . height ) ;
size . width * = ratio ;
size . height * = ratio ;
}
class_desc - > add_image ( icon , size . width , size . height ) ;
}
2024-02-12 16:55:02 +03:00
// Macros for assigning the deprecated/experimental marks to class members in overview.
2023-08-13 02:33:39 +02:00
# define DEPRECATED_DOC_TAG \
2024-02-12 16:55:02 +03:00
class_desc - > push_font ( theme_cache . doc_bold_font ) ; \
2023-08-13 02:33:39 +02:00
class_desc - > push_color ( get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ; \
Ref < Texture2D > error_icon = get_editor_theme_icon ( SNAME ( " StatusError " ) ) ; \
class_desc - > add_image ( error_icon , error_icon - > get_width ( ) , error_icon - > get_height ( ) ) ; \
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( String : : chr ( 160 ) + TTR ( " Deprecated " ) ) ; \
class_desc - > pop ( ) ; \
2022-08-28 07:17:25 +01:00
class_desc - > pop ( ) ;
# define EXPERIMENTAL_DOC_TAG \
2024-02-12 16:55:02 +03:00
class_desc - > push_font ( theme_cache . doc_bold_font ) ; \
2023-08-13 02:33:39 +02:00
class_desc - > push_color ( get_theme_color ( SNAME ( " warning_color " ) , EditorStringName ( Editor ) ) ) ; \
Ref < Texture2D > warning_icon = get_editor_theme_icon ( SNAME ( " NodeWarning " ) ) ; \
2022-08-28 07:17:25 +01:00
class_desc - > add_image ( warning_icon , warning_icon - > get_width ( ) , warning_icon - > get_height ( ) ) ; \
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( String : : chr ( 160 ) + TTR ( " Experimental " ) ) ; \
class_desc - > pop ( ) ; \
2022-08-28 07:17:25 +01:00
class_desc - > pop ( ) ;
2024-02-12 16:55:02 +03:00
// Macros for displaying the deprecated/experimental info in class member descriptions.
# define DEPRECATED_DOC_MSG(m_message, m_default_message) \
Ref < Texture2D > error_icon = get_editor_theme_icon ( SNAME ( " StatusError " ) ) ; \
class_desc - > add_image ( error_icon , error_icon - > get_width ( ) , error_icon - > get_height ( ) ) ; \
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp ) ; \
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ; \
class_desc - > push_font ( theme_cache . doc_bold_font ) ; \
class_desc - > add_text ( TTR ( " Deprecated: " ) ) ; \
class_desc - > pop ( ) ; \
class_desc - > pop ( ) ; \
class_desc - > add_text ( " " ) ; \
if ( ( m_message ) . is_empty ( ) ) { \
class_desc - > add_text ( m_default_message ) ; \
} else { \
_add_text ( m_message ) ; \
}
# define EXPERIMENTAL_DOC_MSG(m_message, m_default_message) \
Ref < Texture2D > warning_icon = get_editor_theme_icon ( SNAME ( " NodeWarning " ) ) ; \
class_desc - > add_image ( warning_icon , warning_icon - > get_width ( ) , warning_icon - > get_height ( ) ) ; \
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp ) ; \
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( get_theme_color ( SNAME ( " warning_color " ) , EditorStringName ( Editor ) ) ) ; \
class_desc - > push_font ( theme_cache . doc_bold_font ) ; \
class_desc - > add_text ( TTR ( " Experimental: " ) ) ; \
class_desc - > pop ( ) ; \
class_desc - > pop ( ) ; \
class_desc - > add_text ( " " ) ; \
if ( ( m_message ) . is_empty ( ) ) { \
class_desc - > add_text ( m_default_message ) ; \
} else { \
_add_text ( m_message ) ; \
}
2023-10-02 20:11:43 +02:00
void EditorHelp : : _add_method ( const DocData : : MethodDoc & p_method , bool p_overview , bool p_override ) {
if ( p_override ) {
method_line [ p_method . name ] = class_desc - > get_paragraph_count ( ) - 2 ; // Gets overridden if description.
}
2017-12-15 09:45:23 +01:00
2022-02-03 21:48:38 +05:45
const bool is_vararg = p_method . qualifiers . contains ( " vararg " ) ;
2017-12-15 09:45:23 +01:00
if ( p_overview ) {
class_desc - > push_cell ( ) ;
2021-11-24 20:58:47 -06:00
class_desc - > push_paragraph ( HORIZONTAL_ALIGNMENT_RIGHT , Control : : TEXT_DIRECTION_AUTO , " " ) ;
2020-05-21 12:01:31 +03:00
} else {
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
2017-12-15 09:45:23 +01:00
}
2023-06-15 17:06:22 +03:00
_add_type ( p_method . return_type , p_method . return_enum , p_method . return_is_bitfield ) ;
2017-12-15 09:45:23 +01:00
if ( p_overview ) {
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // paragraph
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // cell
2017-12-15 09:45:23 +01:00
class_desc - > push_cell ( ) ;
} else {
class_desc - > add_text ( " " ) ;
}
2024-02-16 16:28:05 +03:00
const bool is_documented = p_method . is_deprecated | | p_method . is_experimental | | ! p_method . description . strip_edges ( ) . is_empty ( ) ;
if ( p_overview & & is_documented ) {
2024-03-01 16:40:00 +01:00
class_desc - > push_meta ( " @method " + p_method . name , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
2017-12-15 09:45:23 +01:00
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( p_method . name ) ;
class_desc - > pop ( ) ; // color
2017-12-15 09:45:23 +01:00
2024-02-16 16:28:05 +03:00
if ( p_overview & & is_documented ) {
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // meta
2017-12-15 09:45:23 +01:00
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( " ( " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2017-12-15 09:45:23 +01:00
for ( int j = 0 ; j < p_method . arguments . size ( ) ; j + + ) {
2024-02-16 16:28:05 +03:00
const DocData : : ArgumentDoc & argument = p_method . arguments [ j ] ;
2020-05-14 16:41:43 +02:00
if ( j > 0 ) {
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2017-12-15 09:45:23 +01:00
class_desc - > add_text ( " , " ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
2020-05-14 16:41:43 +02:00
}
2019-09-20 22:40:22 +02:00
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . text_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( argument . name ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( colon_nbsp ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
2024-02-16 16:28:05 +03:00
_add_type ( argument . type , argument . enumeration , argument . is_bitfield ) ;
2024-02-12 16:55:02 +03:00
2024-02-16 16:28:05 +03:00
if ( ! argument . default_value . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp_equal_nbsp ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( argument . default_value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2017-12-15 09:45:23 +01:00
}
}
if ( is_vararg ) {
2024-02-12 16:55:02 +03:00
if ( ! p_method . arguments . is_empty ( ) ) {
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2017-12-15 09:45:23 +01:00
class_desc - > add_text ( " , " ) ;
2024-12-14 21:25:05 +03:00
class_desc - > pop ( ) ; // color
2020-05-14 16:41:43 +02:00
}
2024-02-12 16:55:02 +03:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2017-12-15 09:45:23 +01:00
class_desc - > add_text ( " ... " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2025-03-30 12:59:05 +03:00
const DocData : : ArgumentDoc & rest_argument = p_method . rest_argument ;
class_desc - > push_color ( theme_cache . text_color ) ;
class_desc - > add_text ( rest_argument . name . is_empty ( ) ? " args " : rest_argument . name ) ;
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . symbol_color ) ;
class_desc - > add_text ( colon_nbsp ) ;
class_desc - > pop ( ) ; // color
if ( rest_argument . type . is_empty ( ) ) {
_add_type ( " Array " ) ;
} else {
_add_type ( rest_argument . type , rest_argument . enumeration , rest_argument . is_bitfield ) ;
}
2017-12-15 09:45:23 +01:00
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( " ) " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
2021-12-09 03:42:46 -06:00
if ( ! p_method . qualifiers . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . qualifier_color ) ;
2024-12-14 21:25:05 +03:00
_add_qualifiers_to_rt ( p_method . qualifiers , class_desc ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-08-28 07:17:25 +01:00
}
2020-05-14 16:41:43 +02:00
if ( p_overview ) {
2024-02-12 16:55:02 +03:00
if ( p_method . is_deprecated ) {
class_desc - > add_text ( " " ) ;
DEPRECATED_DOC_TAG ;
}
if ( p_method . is_experimental ) {
class_desc - > add_text ( " " ) ;
EXPERIMENTAL_DOC_TAG ;
}
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // cell
2020-05-14 16:41:43 +02:00
}
2017-12-15 09:45:23 +01:00
}
2021-12-01 21:02:20 +03:00
void EditorHelp : : _add_bulletpoint ( ) {
static const char32_t prefix [ 3 ] = { 0x25CF /* filled circle */ , ' ' , 0 } ;
class_desc - > add_text ( String ( prefix ) ) ;
}
2023-02-11 17:33:38 +01:00
void EditorHelp : : _push_normal_font ( ) {
class_desc - > push_font ( theme_cache . doc_font ) ;
class_desc - > push_font_size ( theme_cache . doc_font_size ) ;
}
void EditorHelp : : _pop_normal_font ( ) {
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // font_size
class_desc - > pop ( ) ; // font
2023-02-11 17:33:38 +01:00
}
void EditorHelp : : _push_title_font ( ) {
class_desc - > push_font ( theme_cache . doc_title_font ) ;
class_desc - > push_font_size ( theme_cache . doc_title_font_size ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( theme_cache . title_color ) ;
2023-02-11 17:33:38 +01:00
}
void EditorHelp : : _pop_title_font ( ) {
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
class_desc - > pop ( ) ; // font_size
class_desc - > pop ( ) ; // font
2023-02-11 17:33:38 +01:00
}
void EditorHelp : : _push_code_font ( ) {
class_desc - > push_font ( theme_cache . doc_code_font ) ;
class_desc - > push_font_size ( theme_cache . doc_code_font_size ) ;
}
void EditorHelp : : _pop_code_font ( ) {
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // font_size
class_desc - > pop ( ) ; // font
2023-02-11 17:33:38 +01:00
}
2023-04-21 09:32:26 -04:00
Error EditorHelp : : _goto_desc ( const String & p_class ) {
2024-08-19 17:03:03 -04:00
if ( ! doc - > class_list . has ( p_class ) ) {
2014-05-06 17:41:19 +08:00
return ERR_DOES_NOT_EXIST ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2014-06-23 23:43:37 +08:00
select_locked = true ;
2014-02-09 22:10:30 -03:00
class_desc - > show ( ) ;
2017-12-18 15:46:17 -02:00
2014-02-20 23:01:44 -03:00
description_line = 0 ;
2014-02-09 22:10:30 -03:00
2020-05-14 16:41:43 +02:00
if ( p_class = = edited_class ) {
2022-04-26 17:57:36 +01:00
return OK ; // Already there.
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2018-08-13 09:40:06 +09:00
edited_class = p_class ;
_update_doc ( ) ;
return OK ;
}
2024-02-12 16:55:02 +03:00
void EditorHelp : : _update_method_list ( MethodType p_method_type , const Vector < DocData : : MethodDoc > & p_methods ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
static const char * titles_by_type [ METHOD_TYPE_MAX ] = {
TTRC ( " Methods " ) ,
TTRC ( " Constructors " ) ,
TTRC ( " Operators " ) ,
} ;
const String title = TTRGET ( titles_by_type [ p_method_type ] ) ;
section_line . push_back ( Pair < String , int > ( title , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
_push_title_font ( ) ;
class_desc - > add_text ( title ) ;
_pop_title_font ( ) ;
class_desc - > add_newline ( ) ;
2021-09-20 21:49:02 -05:00
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
2021-09-20 21:49:02 -05:00
class_desc - > push_indent ( 1 ) ;
2024-02-12 16:55:02 +03:00
_push_code_font ( ) ;
2021-09-20 21:49:02 -05:00
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 ;
2024-02-16 16:28:05 +03:00
for ( const DocData : : MethodDoc & method : p_methods ) {
const String & q = method . qualifiers ;
2022-02-03 21:48:38 +05:45
if ( ( pass = = 0 & & q . contains ( " virtual " ) ) | | ( pass = = 1 & & ! q . contains ( " virtual " ) ) ) {
2024-02-16 16:28:05 +03:00
m . push_back ( method ) ;
2021-09-20 21:49:02 -05:00
}
}
if ( any_previous & & ! m . is_empty ( ) ) {
class_desc - > push_cell ( ) ;
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // cell
2021-09-20 21:49:02 -05:00
class_desc - > push_cell ( ) ;
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // cell
2021-09-20 21:49:02 -05:00
}
String group_prefix ;
for ( int i = 0 ; i < m . size ( ) ; i + + ) {
2024-04-11 11:21:44 +03:00
const String new_prefix = m [ i ] . name . left ( 3 ) ;
2021-09-20 21:49:02 -05:00
bool is_new_group = false ;
2024-04-11 11:21:44 +03:00
if ( i < m . size ( ) - 1 & & new_prefix = = m [ i + 1 ] . name . left ( 3 ) & & new_prefix ! = group_prefix ) {
2021-09-20 21:49:02 -05:00
is_new_group = i > 0 ;
group_prefix = new_prefix ;
2021-12-09 03:42:46 -06:00
} else if ( ! group_prefix . is_empty ( ) & & new_prefix ! = group_prefix ) {
2021-09-20 21:49:02 -05:00
is_new_group = true ;
group_prefix = " " ;
}
if ( is_new_group & & pass = = 1 ) {
class_desc - > push_cell ( ) ;
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // cell
2021-09-20 21:49:02 -05:00
class_desc - > push_cell ( ) ;
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // cell
2021-09-20 21:49:02 -05:00
}
2023-10-02 20:11:43 +02:00
// For constructors always point to the first one.
_add_method ( m [ i ] , true , ( p_method_type ! = METHOD_TYPE_CONSTRUCTOR | | i = = 0 ) ) ;
2021-09-20 21:49:02 -05:00
}
any_previous = ! m . is_empty ( ) ;
}
2023-04-21 09:32:26 -04:00
class_desc - > pop ( ) ; // table
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
2021-09-20 21:49:02 -05:00
}
2024-02-12 16:55:02 +03:00
void EditorHelp : : _update_method_descriptions ( const DocData : : ClassDoc & p_classdoc , MethodType p_method_type , const Vector < DocData : : MethodDoc > & p_methods ) {
2024-04-11 11:21:44 +03:00
# define HANDLE_DOC(m_string) ((p_classdoc.is_script_doc ? (m_string) : DTR(m_string)).strip_edges())
2021-09-20 21:49:02 -05:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
static const char * titles_by_type [ METHOD_TYPE_MAX ] = {
TTRC ( " Method Descriptions " ) ,
TTRC ( " Constructor Descriptions " ) ,
TTRC ( " Operator Descriptions " ) ,
} ;
const String title = TTRGET ( titles_by_type [ p_method_type ] ) ;
section_line . push_back ( Pair < String , int > ( title , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
_push_title_font ( ) ;
class_desc - > add_text ( title ) ;
_pop_title_font ( ) ;
String link_color_text = theme_cache . title_color . to_html ( false ) ;
2021-09-20 21:49:02 -05:00
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 ;
2022-02-03 21:48:38 +05:45
if ( ( pass = = 0 & & q . contains ( " virtual " ) ) | | ( pass = = 1 & & ! q . contains ( " virtual " ) ) ) {
2021-09-20 21:49:02 -05:00
methods_filtered . push_back ( p_methods [ i ] ) ;
}
}
for ( int i = 0 ; i < methods_filtered . size ( ) ; i + + ) {
2024-02-16 16:28:05 +03:00
const DocData : : MethodDoc & method = methods_filtered [ i ] ;
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2023-10-02 20:11:43 +02:00
// For constructors always point to the first one.
2024-02-16 16:28:05 +03:00
_add_method ( method , false , ( p_method_type ! = METHOD_TYPE_CONSTRUCTOR | | i = = 0 ) ) ;
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2021-09-20 21:49:02 -05:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
class_desc - > push_indent ( 1 ) ;
2024-02-12 16:55:02 +03:00
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . text_color ) ;
2024-02-16 16:28:05 +03:00
bool has_prev_text = false ;
if ( method . is_deprecated ) {
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
static const char * messages_by_type [ METHOD_TYPE_MAX ] = {
TTRC ( " This method may be changed or removed in future versions. " ) ,
TTRC ( " This constructor may be changed or removed in future versions. " ) ,
TTRC ( " This operator may be changed or removed in future versions. " ) ,
} ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( method . deprecated_message ) , TTRGET ( messages_by_type [ p_method_type ] ) ) ;
2024-02-12 16:55:02 +03:00
}
2024-02-16 16:28:05 +03:00
if ( method . is_experimental ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
static const char * messages_by_type [ METHOD_TYPE_MAX ] = {
TTRC ( " This method may be changed or removed in future versions. " ) ,
TTRC ( " This constructor may be changed or removed in future versions. " ) ,
TTRC ( " This operator may be changed or removed in future versions. " ) ,
} ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( method . experimental_message ) , TTRGET ( messages_by_type [ p_method_type ] ) ) ;
2024-02-12 16:55:02 +03:00
}
2024-02-16 16:28:05 +03:00
if ( ! method . errors_returned . is_empty ( ) ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( TTR ( " Error codes returned: " ) ) ;
2021-09-20 21:49:02 -05:00
class_desc - > add_newline ( ) ;
class_desc - > push_list ( 0 , RichTextLabel : : LIST_DOTS , false ) ;
2024-02-16 16:28:05 +03:00
for ( int j = 0 ; j < method . errors_returned . size ( ) ; j + + ) {
2021-09-20 21:49:02 -05:00
if ( j > 0 ) {
class_desc - > add_newline ( ) ;
}
2024-02-12 16:55:02 +03:00
2024-02-16 16:28:05 +03:00
int val = method . errors_returned [ j ] ;
2021-09-20 21:49:02 -05:00
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 ;
}
}
2024-02-12 16:55:02 +03:00
class_desc - > push_font ( theme_cache . doc_bold_font ) ;
class_desc - > add_text ( text ) ;
class_desc - > pop ( ) ; // font
2021-09-20 21:49:02 -05:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // list
2021-09-20 21:49:02 -05:00
}
2023-09-05 23:15:34 +08:00
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( method . description ) ;
2024-02-16 16:28:05 +03:00
const bool is_documented = method . is_deprecated | | method . is_experimental | | ! descr . is_empty ( ) ;
2024-02-12 16:55:02 +03:00
if ( ! descr . is_empty ( ) ) {
2024-02-16 16:28:05 +03:00
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
_add_text ( descr ) ;
2024-02-16 16:28:05 +03:00
} else if ( ! is_documented ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2023-09-05 23:15:34 +08:00
String message ;
2021-09-20 21:49:02 -05:00
if ( p_classdoc . is_script_doc ) {
2023-09-05 23:15:34 +08:00
static const char * messages_by_type [ METHOD_TYPE_MAX ] = {
TTRC ( " There is currently no description for this method. " ) ,
TTRC ( " There is currently no description for this constructor. " ) ,
TTRC ( " There is currently no description for this operator. " ) ,
} ;
message = TTRGET ( messages_by_type [ p_method_type ] ) ;
2021-09-20 21:49:02 -05:00
} else {
2023-09-05 23:15:34 +08:00
static const char * messages_by_type [ METHOD_TYPE_MAX ] = {
TTRC ( " There is currently no description for this method. Please help us by [color=$color][url=$url]contributing one[/url][/color]! " ) ,
TTRC ( " There is currently no description for this constructor. Please help us by [color=$color][url=$url]contributing one[/url][/color]! " ) ,
TTRC ( " There is currently no description for this operator. Please help us by [color=$color][url=$url]contributing one[/url][/color]! " ) ,
} ;
message = TTRGET ( messages_by_type [ p_method_type ] ) . replace ( " $url " , CONTRIBUTE_URL ) . replace ( " $color " , link_color_text ) ;
2021-09-20 21:49:02 -05:00
}
2024-02-12 16:55:02 +03:00
class_desc - > add_image ( get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
class_desc - > add_text ( " " ) ;
class_desc - > push_color ( theme_cache . comment_color ) ;
2023-09-05 23:15:34 +08:00
class_desc - > append_text ( message ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2021-09-20 21:49:02 -05:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
_pop_normal_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
2021-09-20 21:49:02 -05:00
}
}
2024-02-12 16:55:02 +03:00
2024-04-11 11:21:44 +03:00
# undef HANDLE_DOC
2021-09-20 21:49:02 -05:00
}
2018-08-13 09:40:06 +09:00
void EditorHelp : : _update_doc ( ) {
2020-05-14 16:41:43 +02:00
if ( ! doc - > class_list . has ( edited_class ) ) {
2018-09-17 00:17:07 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-08-13 09:40:06 +09:00
2014-02-09 22:10:30 -03:00
scroll_locked = true ;
class_desc - > clear ( ) ;
method_line . clear ( ) ;
2017-09-13 19:56:37 -05:00
section_line . clear ( ) ;
2024-02-12 16:55:02 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Top " ) , 0 ) ) ;
2014-02-09 22:10:30 -03:00
2023-02-11 16:29:44 +01:00
String link_color_text = theme_cache . title_color . to_html ( false ) ;
2014-02-09 22:10:30 -03:00
2022-01-18 23:01:30 +03:00
DocData : : ClassDoc cd = doc - > class_list [ edited_class ] ; // Make a copy, so we can sort without worrying.
2024-04-11 11:21:44 +03:00
# define HANDLE_DOC(m_string) ((cd.is_script_doc ? (m_string) : DTR(m_string)).strip_edges())
2024-02-12 16:55:02 +03:00
2018-09-12 17:12:23 +02:00
// Class name
2024-02-12 16:55:02 +03:00
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2024-02-12 16:55:02 +03:00
2016-05-19 00:08:12 +02:00
class_desc - > add_text ( TTR ( " Class: " ) + " " ) ;
2023-03-31 21:17:59 +02:00
_add_type_icon ( edited_class , theme_cache . doc_title_font_size , " Object " ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp ) ;
2024-02-12 16:55:02 +03:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( edited_class ) ;
2022-06-29 07:57:16 +03:00
class_desc - > pop ( ) ; // color
2024-02-12 16:55:02 +03:00
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2022-08-28 07:17:25 +01:00
if ( cd . is_deprecated ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( cd . deprecated_message ) , TTR ( " This class may be changed or removed in future versions. " ) ) ;
2022-08-28 07:17:25 +01:00
}
2024-02-12 16:55:02 +03:00
2022-08-28 07:17:25 +01:00
if ( cd . is_experimental ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( cd . experimental_message ) , TTR ( " This class may be changed or removed in future versions. " ) ) ;
2022-08-28 07:17:25 +01:00
}
2022-09-02 15:56:28 +02:00
2018-09-12 17:12:23 +02:00
// Inheritance tree
// Ascendents
2021-12-09 03:42:46 -06:00
if ( ! cd . inherits . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
_push_normal_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( theme_cache . title_color ) ;
2016-05-19 00:08:12 +02:00
class_desc - > add_text ( TTR ( " Inherits: " ) + " " ) ;
2016-03-05 12:28:25 -03:00
String inherits = cd . inherits ;
2021-12-09 03:42:46 -06:00
while ( ! inherits . is_empty ( ) ) {
2023-03-31 21:17:59 +02:00
_add_type_icon ( inherits , theme_cache . doc_font_size , " ArrowRight " ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp ) ; // Otherwise icon borrows hyperlink from `_add_type()`.
2016-03-05 12:28:25 -03:00
_add_type ( inherits ) ;
2025-02-11 22:01:22 +01:00
const DocData : : ClassDoc * base_class_doc = doc - > class_list . getptr ( inherits ) ;
inherits = base_class_doc ? base_class_doc - > inherits : String ( ) ;
2016-03-05 12:28:25 -03:00
2021-12-09 03:42:46 -06:00
if ( ! inherits . is_empty ( ) ) {
2017-10-09 23:59:53 +07:00
class_desc - > add_text ( " < " ) ;
2016-03-05 12:28:25 -03:00
}
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
_pop_normal_font ( ) ;
2016-03-05 12:28:25 -03:00
}
2023-08-16 14:00:59 +02:00
// Descendants
if ( ( cd . is_script_doc | | ClassDB : : class_exists ( cd . name ) ) & & doc - > inheriting . has ( cd . name ) ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
_push_normal_font ( ) ;
2023-08-16 14:00:59 +02:00
class_desc - > push_color ( theme_cache . title_color ) ;
class_desc - > add_text ( TTR ( " Inherited by: " ) + " " ) ;
2016-03-05 12:28:25 -03:00
2023-08-16 14:00:59 +02:00
for ( RBSet < String , NaturalNoCaseComparator > : : Element * itr = doc - > inheriting [ cd . name ] . front ( ) ; itr ; itr = itr - > next ( ) ) {
if ( itr - > prev ( ) ) {
class_desc - > add_text ( " , " ) ;
2016-03-05 12:28:25 -03:00
}
2023-08-16 14:00:59 +02:00
_add_type_icon ( itr - > get ( ) , theme_cache . doc_font_size , " ArrowRight " ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp ) ; // Otherwise icon borrows hyperlink from `_add_type()`.
2023-08-16 14:00:59 +02:00
_add_type ( itr - > get ( ) ) ;
2016-03-05 12:28:25 -03:00
}
2014-02-09 22:10:30 -03:00
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
2022-08-28 07:17:25 +01:00
}
2022-07-19 18:17:04 +03:00
bool has_description = false ;
2018-09-12 17:12:23 +02:00
// Brief description
2024-04-11 11:21:44 +03:00
const String brief_class_descr = HANDLE_DOC ( cd . brief_description ) ;
2024-02-12 16:55:02 +03:00
if ( ! brief_class_descr . is_empty ( ) ) {
2022-07-19 18:17:04 +03:00
has_description = true ;
2020-01-14 21:38:54 +01:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
class_desc - > push_font ( theme_cache . doc_bold_font ) ;
class_desc - > push_color ( theme_cache . text_color ) ;
_add_text ( brief_class_descr ) ;
class_desc - > pop ( ) ; // color
class_desc - > pop ( ) ; // font
class_desc - > pop ( ) ; // indent
2020-01-14 21:38:54 +01:00
}
// Class description
2024-04-11 11:21:44 +03:00
const String class_descr = HANDLE_DOC ( cd . description ) ;
2024-02-12 16:55:02 +03:00
if ( ! class_descr . is_empty ( ) ) {
2022-07-19 18:17:04 +03:00
has_description = true ;
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-05-18 10:17:55 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Description " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
description_line = class_desc - > get_paragraph_count ( ) - 2 ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2020-01-14 21:38:54 +01:00
class_desc - > add_text ( TTR ( " Description " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2014-02-09 22:10:30 -03:00
2019-06-20 11:42:25 +01:00
class_desc - > add_newline ( ) ;
2014-02-09 22:10:30 -03:00
class_desc - > add_newline ( ) ;
2024-02-12 16:55:02 +03:00
2015-10-20 17:41:27 +02:00
class_desc - > push_indent ( 1 ) ;
2024-02-12 16:55:02 +03:00
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . text_color ) ;
_add_text ( class_descr ) ;
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
_pop_normal_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
}
2023-02-11 17:33:38 +01:00
2024-02-12 16:55:02 +03:00
if ( ! has_description ) {
2020-01-14 21:38:54 +01:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
_push_normal_font ( ) ;
2023-08-13 02:33:39 +02:00
class_desc - > add_image ( get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
2022-07-19 18:17:04 +03:00
class_desc - > add_text ( " " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2022-07-19 18:17:04 +03:00
if ( cd . is_script_doc ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( TTR ( " There is currently no description for this class. " ) ) ;
2022-07-19 18:17:04 +03:00
} else {
class_desc - > append_text ( TTR ( " There is currently no description for this class. Please help us by [color=$color][url=$url]contributing one[/url][/color]! " ) . replace ( " $url " , CONTRIBUTE_URL ) . replace ( " $color " , link_color_text ) ) ;
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-07-19 18:17:04 +03:00
2024-02-12 16:55:02 +03:00
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2023-07-08 13:17:41 +02:00
}
# ifdef MODULE_MONO_ENABLED
if ( classes_with_csharp_differences . has ( cd . name ) ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2025-03-03 22:27:29 -08:00
const String & csharp_differences_url = vformat ( " %s/tutorials/scripting/c_sharp/c_sharp_differences.html " , GODOT_VERSION_DOCS_URL ) ;
2023-07-08 13:17:41 +02:00
class_desc - > push_indent ( 1 ) ;
2024-02-12 16:55:02 +03:00
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . text_color ) ;
2023-07-08 13:17:41 +02:00
2024-02-12 16:55:02 +03:00
class_desc - > append_text ( " [b] " + TTR ( " Note: " ) + " [/b] " + vformat ( TTR ( " There are notable differences when using this API with C#. See [url=%s]C# API differences to GDScript[/url] for more information. " ) , csharp_differences_url ) ) ;
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2022-07-19 18:17:04 +03:00
}
2023-07-08 13:17:41 +02:00
# endif
2022-07-19 18:17:04 +03:00
2020-01-14 21:38:54 +01:00
// Online tutorials
2024-02-12 16:55:02 +03:00
if ( ! cd . tutorials . is_empty ( ) ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2020-01-14 21:38:54 +01:00
class_desc - > add_text ( TTR ( " Online Tutorials " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2020-01-14 21:38:54 +01:00
2014-02-09 22:10:30 -03:00
class_desc - > add_newline ( ) ;
2020-01-14 21:38:54 +01:00
2023-02-11 17:33:38 +01:00
class_desc - > push_indent ( 1 ) ;
_push_code_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2023-02-11 17:33:38 +01:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : TutorialDoc & tutorial : cd . tutorials ) {
2024-04-11 11:21:44 +03:00
const String link = HANDLE_DOC ( tutorial . link ) ;
2024-02-12 16:55:02 +03:00
2024-04-11 11:21:44 +03:00
String link_text = HANDLE_DOC ( tutorial . title ) ;
2024-02-12 16:55:02 +03:00
if ( link_text . is_empty ( ) ) {
const int sep_pos = link . find ( " // " ) ;
if ( sep_pos > = 0 ) {
link_text = link . substr ( sep_pos + 2 ) ;
} else {
link_text = link ;
}
2020-01-14 21:38:54 +01:00
}
2020-01-27 00:58:53 +01:00
class_desc - > add_newline ( ) ;
2024-02-12 16:55:02 +03:00
_add_bulletpoint ( ) ;
class_desc - > append_text ( " [url= " + link + " ] " + link_text + " [/url] " ) ;
2020-01-14 21:38:54 +01:00
}
2020-01-27 00:58:53 +01:00
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
2014-02-09 22:10:30 -03:00
}
2018-09-12 17:12:23 +02:00
// Properties overview
2022-05-19 17:00:06 +02:00
HashSet < String > skip_methods ;
2017-01-04 01:16:14 -03:00
2023-05-13 11:57:44 +02:00
bool has_properties = false ;
bool has_property_descriptions = false ;
for ( const DocData : : PropertyDoc & prop : cd . properties ) {
2024-02-16 16:28:05 +03:00
const bool is_documented = prop . is_deprecated | | prop . is_experimental | | ! prop . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & prop . name . begins_with ( " _ " ) ) {
2023-05-13 11:57:44 +02:00
continue ;
}
has_properties = true ;
if ( ! prop . overridden ) {
has_property_descriptions = true ;
2020-11-29 08:07:57 +05:30
break ;
}
}
if ( has_properties ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-05-18 10:17:55 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Properties " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( TTR ( " Properties " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2017-01-04 01:16:14 -03:00
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2019-06-20 11:42:25 +01:00
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
2017-01-04 01:16:14 -03:00
class_desc - > push_indent ( 1 ) ;
2024-02-12 16:55:02 +03:00
_push_code_font ( ) ;
2021-12-02 22:38:49 +03:00
class_desc - > push_table ( 4 ) ;
2020-05-14 11:00:19 +02:00
class_desc - > set_table_column_expand ( 1 , true ) ;
2017-01-04 01:16:14 -03:00
2024-01-16 16:54:45 +01:00
cd . properties . sort_custom < PropertyCompare > ( ) ;
bool is_generating_overridden_properties = true ; // Set to false as soon as we encounter a non-overridden property.
bool overridden_property_exists = false ;
2024-02-16 16:28:05 +03:00
for ( const DocData : : PropertyDoc & prop : cd . properties ) {
2020-11-29 08:07:57 +05:30
// Ignore undocumented private.
2024-02-16 16:28:05 +03:00
const bool is_documented = prop . is_deprecated | | prop . is_experimental | | ! prop . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & prop . name . begins_with ( " _ " ) ) {
2020-11-29 08:07:57 +05:30
continue ;
}
2024-02-12 16:55:02 +03:00
2024-02-16 16:28:05 +03:00
if ( is_generating_overridden_properties & & ! prop . overridden ) {
2024-01-16 16:54:45 +01:00
is_generating_overridden_properties = false ;
// No need for the extra spacing when there's no overridden property.
if ( overridden_property_exists ) {
class_desc - > push_cell ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // cell
2024-01-16 16:54:45 +01:00
class_desc - > push_cell ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // cell
2024-01-16 16:54:45 +01:00
class_desc - > push_cell ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // cell
2024-01-16 16:54:45 +01:00
class_desc - > push_cell ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // cell
2024-01-16 16:54:45 +01:00
}
}
2024-02-12 16:55:02 +03:00
2024-02-16 16:28:05 +03:00
property_line [ prop . name ] = class_desc - > get_paragraph_count ( ) - 2 ; // Gets overridden if description.
2017-01-04 01:16:14 -03:00
2021-12-02 22:38:49 +03:00
// Property type.
2017-01-04 01:16:14 -03:00
class_desc - > push_cell ( ) ;
2021-11-24 20:58:47 -06:00
class_desc - > push_paragraph ( HORIZONTAL_ALIGNMENT_RIGHT , Control : : TEXT_DIRECTION_AUTO , " " ) ;
2024-02-16 16:28:05 +03:00
_add_type ( prop . type , prop . enumeration , prop . is_bitfield ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // paragraph
2021-12-02 22:38:49 +03:00
class_desc - > pop ( ) ; // cell
2017-01-04 01:16:14 -03:00
bool describe = false ;
2024-02-16 16:28:05 +03:00
if ( ! prop . setter . is_empty ( ) ) {
skip_methods . insert ( prop . setter ) ;
2017-01-04 01:16:14 -03:00
describe = true ;
}
2024-02-16 16:28:05 +03:00
if ( ! prop . getter . is_empty ( ) ) {
skip_methods . insert ( prop . getter ) ;
2017-01-04 01:16:14 -03:00
describe = true ;
}
2024-02-16 16:28:05 +03:00
if ( is_documented ) {
2017-01-04 01:16:14 -03:00
describe = true ;
}
2019-06-01 16:42:22 +03:00
2024-02-16 16:28:05 +03:00
if ( prop . overridden ) {
2019-09-03 13:42:34 +03:00
describe = false ;
}
2021-12-02 22:38:49 +03:00
// Property name.
2017-01-04 01:16:14 -03:00
class_desc - > push_cell ( ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2019-06-01 16:42:22 +03:00
2017-01-04 01:16:14 -03:00
if ( describe ) {
2024-03-01 16:40:00 +01:00
class_desc - > push_meta ( " @member " + prop . name , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
2017-01-04 01:16:14 -03:00
}
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( prop . name ) ;
2017-01-04 01:16:14 -03:00
if ( describe ) {
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // meta
2017-01-04 01:16:14 -03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2021-12-02 22:38:49 +03:00
class_desc - > pop ( ) ; // cell
// Property value.
class_desc - > push_cell ( ) ;
2024-02-16 16:28:05 +03:00
if ( ! prop . default_value . is_empty ( ) ) {
if ( prop . overridden ) {
2024-01-16 16:54:45 +01:00
class_desc - > push_color ( theme_cache . override_color ) ;
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( " [ " ) ;
2024-02-16 16:28:05 +03:00
const String link = vformat ( " [url=@member %s.%s]%s[/url] " , prop . overrides , prop . name , prop . overrides ) ;
2024-02-12 16:55:02 +03:00
class_desc - > append_text ( vformat ( TTR ( " overrides %s: " ) , link ) ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( " " + _fix_constant ( prop . default_value ) + " ] " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2024-01-16 16:54:45 +01:00
overridden_property_exists = true ;
2021-12-02 22:38:49 +03:00
} else {
2024-01-16 16:54:45 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( " [ " + TTR ( " default: " ) + " " ) ;
class_desc - > pop ( ) ; // color
2021-12-02 22:38:49 +03:00
2024-01-16 16:54:45 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( prop . default_value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2021-12-02 22:38:49 +03:00
2024-01-16 16:54:45 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
class_desc - > add_text ( " ] " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2024-01-16 16:54:45 +01:00
}
2022-08-28 07:17:25 +01:00
}
2023-07-02 13:13:38 +03:00
2021-12-02 22:38:49 +03:00
class_desc - > pop ( ) ; // cell
2024-02-12 16:55:02 +03:00
// Property setter/getter and deprecated/experimental marks.
2021-12-02 22:38:49 +03:00
class_desc - > push_cell ( ) ;
2024-02-12 16:55:02 +03:00
bool has_prev_text = false ;
2021-12-02 22:38:49 +03:00
2024-02-16 16:28:05 +03:00
if ( cd . is_script_doc & & ( ! prop . setter . is_empty ( ) | | ! prop . getter . is_empty ( ) ) ) {
2024-02-12 16:55:02 +03:00
has_prev_text = true ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( " [ " + TTR ( " property: " ) + " " ) ;
2020-11-29 08:07:57 +05:30
class_desc - > pop ( ) ; // color
2024-02-16 16:28:05 +03:00
if ( ! prop . setter . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " setter " ) ;
class_desc - > pop ( ) ; // color
}
2024-02-16 16:28:05 +03:00
if ( ! prop . getter . is_empty ( ) ) {
if ( ! prop . setter . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " , " ) ;
class_desc - > pop ( ) ; // color
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " getter " ) ;
class_desc - > pop ( ) ; // color
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " ] " ) ;
class_desc - > pop ( ) ; // color
}
2024-02-16 16:28:05 +03:00
if ( prop . is_deprecated ) {
2024-02-12 16:55:02 +03:00
if ( has_prev_text ) {
class_desc - > add_text ( " " ) ;
}
has_prev_text = true ;
DEPRECATED_DOC_TAG ;
}
2024-02-16 16:28:05 +03:00
if ( prop . is_experimental ) {
2024-02-12 16:55:02 +03:00
if ( has_prev_text ) {
class_desc - > add_text ( " " ) ;
}
has_prev_text = true ;
EXPERIMENTAL_DOC_TAG ;
}
2021-12-02 22:38:49 +03:00
class_desc - > pop ( ) ; // cell
2017-01-04 01:16:14 -03:00
}
2021-12-02 22:38:49 +03:00
class_desc - > pop ( ) ; // table
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
2017-01-04 01:16:14 -03:00
}
2018-09-12 17:12:23 +02:00
// Methods overview
2022-10-18 16:43:37 +02:00
bool sort_methods = EDITOR_GET ( " text_editor/help/sort_functions_alphabetically " ) ;
2016-05-29 11:37:26 -03:00
2017-01-04 01:16:14 -03:00
Vector < DocData : : MethodDoc > methods ;
2014-02-09 22:10:30 -03:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : MethodDoc & method : cd . methods ) {
if ( skip_methods . has ( method . name ) ) {
if ( method . arguments . is_empty ( ) /* getter */ | | ( method . arguments . size ( ) = = 1 & & method . return_type = = " void " /* setter */ ) ) {
2020-01-17 15:10:59 +05:30
continue ;
}
}
2021-08-23 14:53:27 -03:00
// Ignore undocumented non virtual private.
2024-02-16 16:28:05 +03:00
const bool is_documented = method . is_deprecated | | method . is_experimental | | ! method . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & method . name . begins_with ( " _ " ) & & ! method . qualifiers . contains ( " virtual " ) ) {
2020-11-29 08:07:57 +05:30
continue ;
}
2024-02-16 16:28:05 +03:00
methods . push_back ( method ) ;
2017-01-04 01:16:14 -03:00
}
2021-09-20 21:49:02 -05:00
if ( ! cd . constructors . is_empty ( ) ) {
2020-05-14 16:41:43 +02:00
if ( sort_methods ) {
2021-09-20 21:49:02 -05:00
cd . constructors . sort ( ) ;
2020-05-14 16:41:43 +02:00
}
2024-02-12 16:55:02 +03:00
_update_method_list ( METHOD_TYPE_CONSTRUCTOR , cd . constructors ) ;
2021-09-20 21:49:02 -05:00
}
if ( ! methods . is_empty ( ) ) {
if ( sort_methods ) {
methods . sort ( ) ;
}
2024-02-12 16:55:02 +03:00
_update_method_list ( METHOD_TYPE_METHOD , methods ) ;
2021-09-20 21:49:02 -05:00
}
2016-09-07 19:39:02 -03:00
2021-09-20 21:49:02 -05:00
if ( ! cd . operators . is_empty ( ) ) {
if ( sort_methods ) {
cd . operators . sort ( ) ;
2014-02-09 22:10:30 -03:00
}
2024-02-12 16:55:02 +03:00
_update_method_list ( METHOD_TYPE_OPERATOR , cd . operators ) ;
2014-02-09 22:10:30 -03:00
}
2018-09-12 17:12:23 +02:00
// Theme properties
2021-09-20 21:49:02 -05:00
if ( ! cd . theme_properties . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-05-18 10:17:55 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Theme Properties " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( TTR ( " Theme Properties " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2014-06-29 22:41:02 -03:00
2021-12-01 21:02:20 +03:00
String theme_data_type ;
2022-05-13 15:04:37 +02:00
HashMap < String , String > data_type_names ;
2021-12-01 21:02:20 +03:00
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 " ) ;
2014-06-29 22:41:02 -03:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : ThemeItemDoc & theme_item : cd . theme_properties ) {
if ( theme_data_type ! = theme_item . data_type ) {
theme_data_type = theme_item . data_type ;
2021-12-01 21:02:20 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
class_desc - > push_indent ( 1 ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2024-02-12 16:55:02 +03:00
2021-12-01 21:02:20 +03:00
if ( data_type_names . has ( theme_data_type ) ) {
class_desc - > add_text ( data_type_names [ theme_data_type ] ) ;
} else {
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( theme_data_type ) ;
2021-12-01 21:02:20 +03:00
}
2024-02-12 16:55:02 +03:00
_pop_title_font ( ) ;
class_desc - > pop ( ) ; // indent
2021-12-01 21:02:20 +03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-16 16:28:05 +03:00
theme_property_line [ theme_item . name ] = class_desc - > get_paragraph_count ( ) - 2 ; // Gets overridden if description.
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
2021-12-01 21:02:20 +03:00
// Theme item header.
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
// Theme item object type.
2024-02-16 16:28:05 +03:00
_add_type ( theme_item . type ) ;
2017-09-13 19:56:37 -05:00
2021-12-01 21:02:20 +03:00
// Theme item name.
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2021-12-01 21:02:20 +03:00
class_desc - > add_text ( " " ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( theme_item . name ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2019-06-01 16:42:22 +03:00
2021-12-01 21:02:20 +03:00
// Theme item default value.
2024-02-16 16:28:05 +03:00
if ( ! theme_item . default_value . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-01-16 23:47:18 +01:00
class_desc - > add_text ( " [ " + TTR ( " default: " ) + " " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( theme_item . default_value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2019-06-01 16:42:22 +03:00
class_desc - > add_text ( " ] " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2019-06-01 16:42:22 +03:00
}
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2014-02-09 22:10:30 -03:00
2021-12-01 21:02:20 +03:00
// Theme item description.
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . comment_color ) ;
2024-09-04 10:54:50 +03:00
bool has_prev_text = false ;
if ( theme_item . is_deprecated ) {
has_prev_text = true ;
DEPRECATED_DOC_MSG ( HANDLE_DOC ( theme_item . deprecated_message ) , TTR ( " This theme property may be changed or removed in future versions. " ) ) ;
}
if ( theme_item . is_experimental ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( theme_item . experimental_message ) , TTR ( " This theme property may be changed or removed in future versions. " ) ) ;
}
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( theme_item . description ) ;
2024-02-12 16:55:02 +03:00
if ( ! descr . is_empty ( ) ) {
2024-09-04 10:54:50 +03:00
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
_add_text ( descr ) ;
2024-09-04 10:54:50 +03:00
} else if ( ! has_prev_text ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_image ( get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
class_desc - > add_text ( " " ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2024-02-12 16:55:02 +03:00
if ( cd . is_script_doc ) {
class_desc - > add_text ( TTR ( " There is currently no description for this theme property. " ) ) ;
} else {
class_desc - > append_text ( TTR ( " There is currently no description for this theme property. Please help us by [color=$color][url=$url]contributing one[/url][/color]! " ) . replace ( " $url " , CONTRIBUTE_URL ) . replace ( " $color " , link_color_text ) ) ;
}
2021-12-01 21:02:20 +03:00
class_desc - > pop ( ) ; // color
2014-06-29 22:41:02 -03:00
}
2021-12-01 21:02:20 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2014-06-29 22:41:02 -03:00
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
}
2014-06-29 22:41:02 -03:00
}
2016-08-01 16:30:09 +08:00
2018-09-12 17:12:23 +02:00
// Signals
2021-09-20 21:49:02 -05:00
if ( ! cd . signals . is_empty ( ) ) {
2016-05-29 11:37:26 -03:00
if ( sort_methods ) {
cd . signals . sort ( ) ;
}
2017-09-13 19:56:37 -05:00
2025-04-23 12:45:19 -04:00
bool header_added = false ;
2014-02-09 22:10:30 -03:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : MethodDoc & signal : cd . signals ) {
2025-04-23 12:45:19 -04:00
// Ignore undocumented private.
const bool is_documented = signal . is_deprecated | | signal . is_experimental | | ! signal . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & signal . name . begins_with ( " _ " ) ) {
continue ;
}
if ( ! header_added ) {
header_added = true ;
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
section_line . push_back ( Pair < String , int > ( TTR ( " Signals " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
_push_title_font ( ) ;
class_desc - > add_text ( TTR ( " Signals " ) ) ;
_pop_title_font ( ) ;
}
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-16 16:28:05 +03:00
signal_line [ signal . name ] = class_desc - > get_paragraph_count ( ) - 2 ; // Gets overridden if description.
2021-12-01 21:02:20 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
// Signal header.
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( signal . name ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( " ( " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2024-02-16 16:28:05 +03:00
for ( int j = 0 ; j < signal . arguments . size ( ) ; j + + ) {
const DocData : : ArgumentDoc & argument = signal . arguments [ j ] ;
2020-05-14 16:41:43 +02:00
if ( j > 0 ) {
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2014-02-09 22:10:30 -03:00
class_desc - > add_text ( " , " ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
2020-05-14 16:41:43 +02:00
}
2019-09-20 22:40:22 +02:00
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . text_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( argument . name ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( colon_nbsp ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
2024-02-16 16:28:05 +03:00
_add_type ( argument . type , argument . enumeration , argument . is_bitfield ) ;
2024-02-12 16:55:02 +03:00
// Signals currently do not support default argument values, neither the core nor GDScript.
// This code is just for completeness.
2024-02-16 16:28:05 +03:00
if ( ! argument . default_value . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp_equal_nbsp ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( argument . default_value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2014-02-09 22:10:30 -03:00
}
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( " ) " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_code_font ( ) ;
class_desc - > add_newline ( ) ;
// Signal description.
class_desc - > push_indent ( 1 ) ;
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . comment_color ) ;
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( signal . description ) ;
2024-02-12 16:55:02 +03:00
const bool is_multiline = descr . find_char ( ' \n ' ) > 0 ;
2024-02-16 16:28:05 +03:00
bool has_prev_text = false ;
2022-08-28 07:17:25 +01:00
2024-02-16 16:28:05 +03:00
if ( signal . is_deprecated ) {
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( signal . deprecated_message ) , TTR ( " This signal may be changed or removed in future versions. " ) ) ;
2022-08-28 07:17:25 +01:00
}
2023-07-02 13:13:38 +03:00
2024-02-16 16:28:05 +03:00
if ( signal . is_experimental ) {
if ( has_prev_text ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2024-02-16 16:28:05 +03:00
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
2024-02-12 16:55:02 +03:00
}
2024-02-16 16:28:05 +03:00
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( signal . experimental_message ) , TTR ( " This signal may be changed or removed in future versions. " ) ) ;
2022-08-28 07:17:25 +01:00
}
2024-02-12 16:55:02 +03:00
if ( ! descr . is_empty ( ) ) {
2024-02-16 16:28:05 +03:00
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
_add_text ( descr ) ;
2024-02-16 16:28:05 +03:00
} else if ( ! has_prev_text ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_image ( get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
class_desc - > add_text ( " " ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2024-02-12 16:55:02 +03:00
if ( cd . is_script_doc ) {
class_desc - > add_text ( TTR ( " There is currently no description for this signal. " ) ) ;
} else {
class_desc - > append_text ( TTR ( " There is currently no description for this signal. Please help us by [color=$color][url=$url]contributing one[/url][/color]! " ) . replace ( " $url " , CONTRIBUTE_URL ) . replace ( " $color " , link_color_text ) ) ;
}
2023-02-11 17:33:38 +01:00
class_desc - > pop ( ) ; // color
2014-02-09 22:10:30 -03:00
}
2023-02-11 17:33:38 +01:00
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2014-02-09 22:10:30 -03:00
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
}
2014-02-09 22:10:30 -03:00
}
2018-09-12 17:12:23 +02:00
// Constants and enums
2021-09-20 21:49:02 -05:00
if ( ! cd . constants . is_empty ( ) ) {
2022-05-13 15:04:37 +02:00
HashMap < String , Vector < DocData : : ConstantDoc > > enums ;
2017-08-23 19:10:32 -03:00
Vector < DocData : : ConstantDoc > constants ;
2014-02-09 22:10:30 -03:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : ConstantDoc & constant : cd . constants ) {
if ( ! constant . enumeration . is_empty ( ) ) {
if ( ! enums . has ( constant . enumeration ) ) {
enums [ constant . enumeration ] = Vector < DocData : : ConstantDoc > ( ) ;
2017-08-23 19:10:32 -03:00
}
2024-02-16 16:28:05 +03:00
enums [ constant . enumeration ] . push_back ( constant ) ;
2017-08-23 19:10:32 -03:00
} else {
2020-11-29 08:07:57 +05:30
// Ignore undocumented private.
2024-02-16 16:28:05 +03:00
const bool is_documented = constant . is_deprecated | | constant . is_experimental | | ! constant . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & constant . name . begins_with ( " _ " ) ) {
2020-11-29 08:07:57 +05:30
continue ;
}
2024-02-16 16:28:05 +03:00
constants . push_back ( constant ) ;
2017-08-23 19:10:32 -03:00
}
}
2018-09-12 17:12:23 +02:00
// Enums
2023-11-02 22:36:04 -05:00
bool has_enums = enums . size ( ) & & ! cd . is_script_doc ;
if ( enums . size ( ) & & ! has_enums ) {
for ( KeyValue < String , DocData : : EnumDoc > & E : cd . enums ) {
2024-02-16 16:28:05 +03:00
const bool is_documented = E . value . is_deprecated | | E . value . is_experimental | | ! E . value . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & E . key . begins_with ( " _ " ) ) {
2023-11-02 22:36:04 -05:00
continue ;
}
has_enums = true ;
break ;
}
}
if ( has_enums ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-05-18 10:17:55 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Enumerations " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( TTR ( " Enumerations " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2017-08-23 19:10:32 -03:00
2021-08-09 14:13:42 -06:00
for ( KeyValue < String , Vector < DocData : : ConstantDoc > > & E : enums ) {
2023-11-02 22:36:04 -05:00
String key = E . key ;
2024-11-16 17:16:07 +01:00
if ( ( key . get_slice_count ( " . " ) > 1 ) & & ( key . get_slicec ( ' . ' , 0 ) = = edited_class ) ) {
key = key . get_slicec ( ' . ' , 1 ) ;
2023-11-02 22:36:04 -05:00
}
if ( cd . enums . has ( key ) ) {
2024-02-16 16:28:05 +03:00
const bool is_documented = cd . enums [ key ] . is_deprecated | | cd . enums [ key ] . is_experimental | | ! cd . enums [ key ] . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & cd . is_script_doc & & E . key . begins_with ( " _ " ) ) {
2023-11-02 22:36:04 -05:00
continue ;
}
}
2017-08-23 19:10:32 -03:00
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
// Enum header.
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2023-07-02 13:13:38 +03:00
2024-02-12 16:55:02 +03:00
enum_line [ E . key ] = class_desc - > get_paragraph_count ( ) - 2 ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . title_color ) ;
2022-06-24 11:16:37 +02:00
if ( E . value . size ( ) & & E . value [ 0 ] . is_bitfield ) {
2024-02-27 17:09:55 +03:00
class_desc - > add_text ( " flags " ) ;
2022-06-24 11:16:37 +02:00
} else {
2024-02-27 17:09:55 +03:00
class_desc - > add_text ( " enum " ) ;
2022-06-24 11:16:37 +02:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2023-11-02 22:36:04 -05:00
class_desc - > add_text ( key ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2017-08-23 19:10:32 -03:00
class_desc - > add_text ( " : " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-07-02 13:13:38 +03:00
_pop_code_font ( ) ;
2020-11-29 08:07:57 +05:30
// Enum description.
2024-02-12 16:55:02 +03:00
if ( key ! = " @unnamed_enums " & & cd . enums . has ( key ) ) {
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( cd . enums [ key ] . description ) ;
2024-02-12 16:55:02 +03:00
const bool is_multiline = descr . find_char ( ' \n ' ) > 0 ;
if ( cd . enums [ key ] . is_deprecated | | cd . enums [ key ] . is_experimental | | ! descr . is_empty ( ) ) {
class_desc - > add_newline ( ) ;
2023-02-11 17:33:38 +01:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . text_color ) ;
bool has_prev_text = false ;
if ( cd . enums [ key ] . is_deprecated ) {
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( cd . enums [ key ] . deprecated_message ) , TTR ( " This enumeration may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
if ( cd . enums [ key ] . is_experimental ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( cd . enums [ key ] . experimental_message ) , TTR ( " This enumeration may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
if ( ! descr . is_empty ( ) ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
_add_text ( descr ) ;
}
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
}
2020-11-29 08:07:57 +05:30
}
2024-02-12 16:55:02 +03:00
HashMap < String , int > enum_values ;
const int enum_start_line = enum_line [ E . key ] ;
2017-08-23 19:10:32 -03:00
2024-02-12 16:55:02 +03:00
bool prev_is_multiline = true ; // Use a large margin for the first item.
2024-02-16 16:28:05 +03:00
for ( const DocData : : ConstantDoc & enum_value : E . value ) {
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( enum_value . description ) ;
2024-02-12 16:55:02 +03:00
const bool is_multiline = descr . find_char ( ' \n ' ) > 0 ;
class_desc - > add_newline ( ) ;
if ( prev_is_multiline | | is_multiline ) {
class_desc - > add_newline ( ) ;
}
prev_is_multiline = is_multiline ;
2020-05-14 16:41:43 +02:00
if ( cd . name = = " @GlobalScope " ) {
2024-02-16 16:28:05 +03:00
enum_values [ enum_value . name ] = enum_start_line ;
2020-05-14 16:41:43 +02:00
}
2017-08-23 19:10:32 -03:00
2022-04-26 17:57:36 +01:00
// Add the enum constant line to the constant_line map so we can locate it as a constant.
2024-02-16 16:28:05 +03:00
constant_line [ enum_value . name ] = class_desc - > get_paragraph_count ( ) - 2 ;
2019-03-27 20:01:16 +01:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
2023-07-02 13:13:38 +03:00
2024-02-12 16:55:02 +03:00
// Enum value header.
_push_code_font ( ) ;
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
2024-02-12 16:55:02 +03:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( enum_value . name ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp_equal_nbsp ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( enum_value . value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-08-28 07:17:25 +01:00
2023-07-02 13:13:38 +03:00
_pop_code_font ( ) ;
2024-02-12 16:55:02 +03:00
// Enum value description.
2024-02-16 16:28:05 +03:00
if ( enum_value . is_deprecated | | enum_value . is_experimental | | ! descr . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2021-12-01 21:02:20 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
2023-02-11 17:33:38 +01:00
_push_normal_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( theme_cache . comment_color ) ;
bool has_prev_text = false ;
2024-02-16 16:28:05 +03:00
if ( enum_value . is_deprecated ) {
2024-02-12 16:55:02 +03:00
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( enum_value . deprecated_message ) , TTR ( " This constant may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
2024-02-16 16:28:05 +03:00
if ( enum_value . is_experimental ) {
2024-02-12 16:55:02 +03:00
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( enum_value . experimental_message ) , TTR ( " This constant may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
if ( ! descr . is_empty ( ) ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
_add_text ( descr ) ;
2020-05-21 12:01:31 +03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2017-08-23 19:10:32 -03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
2017-08-23 19:10:32 -03:00
}
2020-05-14 16:41:43 +02:00
if ( cd . name = = " @GlobalScope " ) {
2024-02-12 16:55:02 +03:00
enum_values_line [ E . key ] = enum_values ;
2020-05-14 16:41:43 +02:00
}
2017-08-23 19:10:32 -03:00
}
}
2018-09-12 17:12:23 +02:00
// Constants
2024-02-12 16:55:02 +03:00
if ( ! constants . is_empty ( ) ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-05-18 10:17:55 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Constants " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( TTR ( " Constants " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2017-08-23 19:10:32 -03:00
2024-02-12 16:55:02 +03:00
bool prev_is_multiline = true ; // Use a large margin for the first item.
2024-02-16 16:28:05 +03:00
for ( const DocData : : ConstantDoc & constant : constants ) {
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( constant . description ) ;
2024-02-12 16:55:02 +03:00
const bool is_multiline = descr . find_char ( ' \n ' ) > 0 ;
class_desc - > add_newline ( ) ;
if ( prev_is_multiline | | is_multiline ) {
class_desc - > add_newline ( ) ;
}
prev_is_multiline = is_multiline ;
2024-02-16 16:28:05 +03:00
constant_line [ constant . name ] = class_desc - > get_paragraph_count ( ) - 2 ;
2023-02-11 17:33:38 +01:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
// Constant header.
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2017-12-15 16:43:27 +01:00
2024-02-16 16:28:05 +03:00
if ( constant . value . begins_with ( " Color( " ) & & constant . value . ends_with ( " ) " ) ) {
2024-05-28 12:55:07 +02:00
String stripped = constant . value . remove_char ( ' ' ) . replace ( " Color( " , " " ) . remove_char ( ' ) ' ) ;
2022-11-20 12:29:50 +01:00
PackedFloat64Array color = stripped . split_floats ( " , " ) ;
2017-12-15 16:43:27 +01:00
if ( color . size ( ) > = 3 ) {
class_desc - > push_color ( Color ( color [ 0 ] , color [ 1 ] , color [ 2 ] ) ) ;
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2017-12-15 16:43:27 +01:00
}
2020-05-21 12:01:31 +03:00
} else {
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
2017-12-15 16:43:27 +01:00
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( constant . name ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp_equal_nbsp ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( constant . value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-08-28 07:17:25 +01:00
2023-07-02 13:13:38 +03:00
_pop_code_font ( ) ;
2024-02-12 16:55:02 +03:00
// Constant description.
2024-02-16 16:28:05 +03:00
if ( constant . is_deprecated | | constant . is_experimental | | ! descr . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
2021-12-01 21:02:20 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
2023-02-11 17:33:38 +01:00
_push_normal_font ( ) ;
2024-02-12 16:55:02 +03:00
class_desc - > push_color ( theme_cache . comment_color ) ;
bool has_prev_text = false ;
2024-02-16 16:28:05 +03:00
if ( constant . is_deprecated ) {
2024-02-12 16:55:02 +03:00
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( constant . deprecated_message ) , TTR ( " This constant may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
2024-02-16 16:28:05 +03:00
if ( constant . is_experimental ) {
2024-02-12 16:55:02 +03:00
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( constant . experimental_message ) , TTR ( " This constant may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
if ( ! descr . is_empty ( ) ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
if ( is_multiline ) {
class_desc - > add_newline ( ) ;
}
}
has_prev_text = true ;
_add_text ( descr ) ;
2020-05-21 12:01:31 +03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2017-08-23 19:10:32 -03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // indent
2014-02-09 22:10:30 -03:00
}
}
}
2022-07-04 18:56:34 +03:00
// Annotations
if ( ! cd . annotations . is_empty ( ) ) {
if ( sort_methods ) {
cd . annotations . sort ( ) ;
}
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-07-04 18:56:34 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Annotations " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( TTR ( " Annotations " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2022-07-04 18:56:34 +03:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : MethodDoc & annotation : cd . annotations ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-16 16:28:05 +03:00
annotation_line [ annotation . name ] = class_desc - > get_paragraph_count ( ) - 2 ; // Gets overridden if description.
2022-07-04 18:56:34 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > push_indent ( 1 ) ;
// Annotation header.
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2022-07-04 18:56:34 +03:00
_add_bulletpoint ( ) ;
2024-02-12 16:55:02 +03:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( annotation . name ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
2024-02-16 16:28:05 +03:00
if ( ! annotation . arguments . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( " ( " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2024-02-16 16:28:05 +03:00
for ( int j = 0 ; j < annotation . arguments . size ( ) ; j + + ) {
const DocData : : ArgumentDoc & argument = annotation . arguments [ j ] ;
2022-07-04 18:56:34 +03:00
if ( j > 0 ) {
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( " , " ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
}
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . text_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( argument . name ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( colon_nbsp ) ;
2025-03-30 12:59:05 +03:00
class_desc - > pop ( ) ; // color
2024-04-15 22:10:47 +03:00
_add_type ( argument . type , argument . enumeration , argument . is_bitfield ) ;
2024-02-12 16:55:02 +03:00
2024-02-16 16:28:05 +03:00
if ( ! argument . default_value . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2024-12-14 21:25:05 +03:00
class_desc - > add_text ( nbsp_equal_nbsp ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( argument . default_value ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
}
}
2024-02-16 16:28:05 +03:00
if ( annotation . qualifiers . contains ( " vararg " ) ) {
if ( ! annotation . arguments . is_empty ( ) ) {
2025-03-30 12:59:05 +03:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( " , " ) ;
2024-12-14 21:25:05 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
}
2024-02-12 16:55:02 +03:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( " ... " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2025-03-30 12:59:05 +03:00
const DocData : : ArgumentDoc & rest_argument = annotation . rest_argument ;
class_desc - > push_color ( theme_cache . text_color ) ;
class_desc - > add_text ( rest_argument . name . is_empty ( ) ? " args " : rest_argument . name ) ;
class_desc - > pop ( ) ; // color
class_desc - > push_color ( theme_cache . symbol_color ) ;
class_desc - > add_text ( colon_nbsp ) ;
class_desc - > pop ( ) ; // color
if ( rest_argument . type . is_empty ( ) ) {
_add_type ( " Array " ) ;
} else {
_add_type ( rest_argument . type , rest_argument . enumeration , rest_argument . is_bitfield ) ;
}
2022-07-04 18:56:34 +03:00
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( " ) " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
}
2024-02-16 16:28:05 +03:00
if ( ! annotation . qualifiers . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . qualifier_color ) ;
2024-12-14 21:25:05 +03:00
_add_qualifiers_to_rt ( annotation . qualifiers , class_desc ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
}
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2022-07-04 18:56:34 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
// Annotation description.
class_desc - > push_indent ( 1 ) ;
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . comment_color ) ;
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( annotation . description ) ;
2024-02-12 16:55:02 +03:00
if ( ! descr . is_empty ( ) ) {
_add_text ( descr ) ;
2022-07-04 18:56:34 +03:00
} else {
2023-08-13 02:33:39 +02:00
class_desc - > add_image ( get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
2022-07-04 18:56:34 +03:00
class_desc - > add_text ( " " ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2022-07-04 18:56:34 +03:00
if ( cd . is_script_doc ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( TTR ( " There is currently no description for this annotation. " ) ) ;
2022-07-04 18:56:34 +03:00
} 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 ) ) ;
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2022-07-04 18:56:34 +03:00
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
class_desc - > pop ( ) ; // indent
}
2022-07-04 18:56:34 +03:00
}
2018-09-12 17:12:23 +02:00
// Property descriptions
2023-05-13 11:57:44 +02:00
if ( has_property_descriptions ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-05-18 10:17:55 +03:00
section_line . push_back ( Pair < String , int > ( TTR ( " Property Descriptions " ) , class_desc - > get_paragraph_count ( ) - 2 ) ) ;
2023-02-11 17:33:38 +01:00
_push_title_font ( ) ;
2019-09-20 22:40:22 +02:00
class_desc - > add_text ( TTR ( " Property Descriptions " ) ) ;
2023-02-11 17:33:38 +01:00
_pop_title_font ( ) ;
2017-01-04 01:16:14 -03:00
2024-02-16 16:28:05 +03:00
for ( const DocData : : PropertyDoc & prop : cd . properties ) {
if ( prop . overridden ) {
2019-09-03 13:42:34 +03:00
continue ;
2020-05-14 16:41:43 +02:00
}
2022-10-21 13:06:53 +02:00
// Ignore undocumented private.
2024-02-16 16:28:05 +03:00
const bool is_documented = prop . is_deprecated | | prop . is_experimental | | ! prop . description . strip_edges ( ) . is_empty ( ) ;
if ( ! is_documented & & prop . name . begins_with ( " _ " ) ) {
2022-10-21 13:06:53 +02:00
continue ;
}
2019-09-03 13:42:34 +03:00
2024-02-12 16:55:02 +03:00
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2024-02-16 16:28:05 +03:00
property_line [ prop . name ] = class_desc - > get_paragraph_count ( ) - 2 ;
2017-01-04 01:16:14 -03:00
2017-11-04 14:09:45 +01:00
class_desc - > push_table ( 2 ) ;
2020-05-14 11:00:19 +02:00
class_desc - > set_table_column_expand ( 1 , true ) ;
2017-11-04 14:09:45 +01:00
class_desc - > push_cell ( ) ;
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2021-12-01 21:02:20 +03:00
_add_bulletpoint ( ) ;
2024-02-16 16:28:05 +03:00
_add_type ( prop . type , prop . enumeration , prop . is_bitfield ) ;
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2017-11-04 14:09:45 +01:00
class_desc - > pop ( ) ; // cell
class_desc - > push_cell ( ) ;
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2024-02-12 16:55:02 +03:00
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . headline_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( prop . name ) ;
2017-11-04 14:09:45 +01:00
class_desc - > pop ( ) ; // color
2019-06-01 16:42:22 +03:00
2024-02-16 16:28:05 +03:00
if ( ! prop . default_value . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-01-16 23:47:18 +01:00
class_desc - > add_text ( " [ " + TTR ( " default: " ) + " " ) ;
2019-06-01 16:42:22 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( _fix_constant ( prop . default_value ) ) ;
2019-06-01 16:42:22 +03:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2019-06-01 16:42:22 +03:00
class_desc - > add_text ( " ] " ) ;
class_desc - > pop ( ) ; // color
2022-08-28 07:17:25 +01:00
}
2024-02-16 16:28:05 +03:00
if ( cd . is_script_doc & & ( ! prop . setter . is_empty ( ) | | ! prop . getter . is_empty ( ) ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " [ " + TTR ( " property: " ) + " " ) ;
class_desc - > pop ( ) ; // color
2024-02-16 16:28:05 +03:00
if ( ! prop . setter . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " setter " ) ;
class_desc - > pop ( ) ; // color
}
2024-02-16 16:28:05 +03:00
if ( ! prop . getter . is_empty ( ) ) {
if ( ! prop . setter . is_empty ( ) ) {
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " , " ) ;
class_desc - > pop ( ) ; // color
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . value_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " getter " ) ;
class_desc - > pop ( ) ; // color
}
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . symbol_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " ] " ) ;
class_desc - > pop ( ) ; // color
}
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2017-11-04 14:09:45 +01:00
class_desc - > pop ( ) ; // cell
2017-01-04 01:16:14 -03:00
2020-11-29 08:07:57 +05:30
// Script doc doesn't have setter, getter.
if ( ! cd . is_script_doc ) {
2022-05-13 15:04:37 +02:00
HashMap < String , DocData : : MethodDoc > method_map ;
2020-11-29 08:07:57 +05:30
for ( int j = 0 ; j < methods . size ( ) ; j + + ) {
method_map [ methods [ j ] . name ] = methods [ j ] ;
}
2020-05-21 21:07:44 +02:00
2024-02-16 16:28:05 +03:00
if ( ! prop . setter . is_empty ( ) ) {
2020-11-29 08:07:57 +05:30
class_desc - > push_cell ( ) ;
class_desc - > pop ( ) ; // cell
2017-01-04 01:16:14 -03:00
2020-11-29 08:07:57 +05:30
class_desc - > push_cell ( ) ;
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . text_color ) ;
2023-02-11 17:33:38 +01:00
2024-02-16 16:28:05 +03:00
if ( method_map [ prop . setter ] . arguments . size ( ) > 1 ) {
2020-11-29 08:07:57 +05:30
// Setters with additional arguments are exposed in the method list, so we link them here for quick access.
2024-02-16 16:28:05 +03:00
class_desc - > push_meta ( " @method " + prop . setter ) ;
class_desc - > add_text ( prop . setter + TTR ( " (value) " ) ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // meta
2020-11-29 08:07:57 +05:30
} else {
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( prop . setter + TTR ( " (value) " ) ) ;
2020-11-29 08:07:57 +05:30
}
2023-02-11 17:33:38 +01:00
2020-11-29 08:07:57 +05:30
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " setter " ) ;
class_desc - > pop ( ) ; // color
2023-02-11 17:33:38 +01:00
_pop_code_font ( ) ;
2020-11-29 08:07:57 +05:30
class_desc - > pop ( ) ; // cell
2023-02-11 17:33:38 +01:00
2024-02-16 16:28:05 +03:00
method_line [ prop . setter ] = property_line [ prop . name ] ;
2020-05-21 21:07:44 +02:00
}
2017-01-04 01:16:14 -03:00
2024-02-16 16:28:05 +03:00
if ( ! prop . getter . is_empty ( ) ) {
2020-11-29 08:07:57 +05:30
class_desc - > push_cell ( ) ;
class_desc - > pop ( ) ; // cell
2017-01-04 01:16:14 -03:00
2020-11-29 08:07:57 +05:30
class_desc - > push_cell ( ) ;
2023-02-11 17:33:38 +01:00
_push_code_font ( ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . text_color ) ;
2023-02-11 17:33:38 +01:00
2024-02-16 16:28:05 +03:00
if ( ! method_map [ prop . getter ] . arguments . is_empty ( ) ) {
2020-11-29 08:07:57 +05:30
// Getters with additional arguments are exposed in the method list, so we link them here for quick access.
2024-02-16 16:28:05 +03:00
class_desc - > push_meta ( " @method " + prop . getter ) ;
class_desc - > add_text ( prop . getter + " () " ) ;
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // meta
2020-11-29 08:07:57 +05:30
} else {
2024-02-16 16:28:05 +03:00
class_desc - > add_text ( prop . getter + " () " ) ;
2020-11-29 08:07:57 +05:30
}
2023-02-11 17:33:38 +01:00
class_desc - > pop ( ) ; // color
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2020-11-29 08:07:57 +05:30
class_desc - > add_text ( " getter " ) ;
2023-02-11 17:33:38 +01:00
class_desc - > pop ( ) ; // color
_pop_code_font ( ) ;
class_desc - > pop ( ) ; // cell
2024-02-16 16:28:05 +03:00
method_line [ prop . getter ] = property_line [ prop . name ] ;
2020-05-21 21:07:44 +02:00
}
2017-01-04 01:16:14 -03:00
}
2017-11-04 14:09:45 +01:00
class_desc - > pop ( ) ; // table
2019-06-20 11:42:25 +01:00
class_desc - > add_newline ( ) ;
2017-01-04 01:16:14 -03:00
class_desc - > add_newline ( ) ;
class_desc - > push_indent ( 1 ) ;
2024-02-12 16:55:02 +03:00
_push_normal_font ( ) ;
class_desc - > push_color ( theme_cache . text_color ) ;
2024-02-16 16:28:05 +03:00
bool has_prev_text = false ;
if ( prop . is_deprecated ) {
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
DEPRECATED_DOC_MSG ( HANDLE_DOC ( prop . deprecated_message ) , TTR ( " This property may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
2024-02-16 16:28:05 +03:00
if ( prop . is_experimental ) {
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2024-04-11 11:21:44 +03:00
EXPERIMENTAL_DOC_MSG ( HANDLE_DOC ( prop . experimental_message ) , TTR ( " This property may be changed or removed in future versions. " ) ) ;
2024-02-12 16:55:02 +03:00
}
2024-04-11 11:21:44 +03:00
const String descr = HANDLE_DOC ( prop . description ) ;
2024-02-12 16:55:02 +03:00
if ( ! descr . is_empty ( ) ) {
2024-02-16 16:28:05 +03:00
if ( has_prev_text ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
}
has_prev_text = true ;
2024-02-12 16:55:02 +03:00
_add_text ( descr ) ;
2024-02-16 16:28:05 +03:00
} else if ( ! has_prev_text ) {
2023-08-13 02:33:39 +02:00
class_desc - > add_image ( get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
2017-09-12 19:16:18 -03:00
class_desc - > add_text ( " " ) ;
2023-02-11 16:29:44 +01:00
class_desc - > push_color ( theme_cache . comment_color ) ;
2020-11-29 08:07:57 +05:30
if ( cd . is_script_doc ) {
2024-02-12 16:55:02 +03:00
class_desc - > add_text ( TTR ( " There is currently no description for this property. " ) ) ;
2020-11-29 08:07:57 +05:30
} else {
2020-05-29 16:25:12 +02:00
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 ) ) ;
2020-11-29 08:07:57 +05:30
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
2017-09-12 19:16:18 -03:00
}
2023-02-11 17:33:38 +01:00
2024-09-04 10:54:50 +03:00
// Add copy note to built-in properties returning `Packed*Array`.
if ( ! cd . is_script_doc & & packed_array_types . has ( prop . type ) ) {
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
_add_text ( vformat ( TTR ( " [b]Note:[/b] The returned array is [i]copied[/i] and any changes to it will not update the original property value. See [%s] for more details. " ) , prop . type ) ) ;
}
2024-02-12 16:55:02 +03:00
class_desc - > pop ( ) ; // color
_pop_normal_font ( ) ;
class_desc - > pop ( ) ; // indent
2017-01-04 01:16:14 -03:00
}
}
2021-09-20 21:49:02 -05:00
// Constructor descriptions
2022-07-19 18:17:04 +03:00
if ( ! cd . constructors . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
_update_method_descriptions ( cd , METHOD_TYPE_CONSTRUCTOR , cd . constructors ) ;
2021-09-20 21:49:02 -05:00
}
2018-09-12 17:12:23 +02:00
// Method descriptions
2022-07-19 18:17:04 +03:00
if ( ! methods . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
_update_method_descriptions ( cd , METHOD_TYPE_METHOD , methods ) ;
2021-09-20 21:49:02 -05:00
}
2019-10-03 12:31:41 -04:00
2021-09-20 21:49:02 -05:00
// Operator descriptions
2022-07-19 18:17:04 +03:00
if ( ! cd . operators . is_empty ( ) ) {
2024-02-12 16:55:02 +03:00
_update_method_descriptions ( cd , METHOD_TYPE_OPERATOR , cd . operators ) ;
2014-02-09 22:10:30 -03:00
}
2022-07-19 18:17:04 +03:00
2024-02-12 16:55:02 +03:00
// Allow the document to be scrolled slightly below the end.
class_desc - > add_newline ( ) ;
class_desc - > add_newline ( ) ;
2022-07-19 18:17:04 +03:00
// Free the scroll.
2014-02-09 22:10:30 -03:00
scroll_locked = false ;
2024-02-12 16:55:02 +03:00
2024-04-11 11:21:44 +03:00
# undef HANDLE_DOC
2014-02-09 22:10:30 -03:00
}
2014-06-23 23:43:37 +08:00
void EditorHelp : : _request_help ( const String & p_string ) {
2014-05-06 17:41:19 +08:00
Error err = _goto_desc ( p_string ) ;
if ( err = = OK ) {
2024-08-19 18:08:31 -04:00
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > select ( EditorMainScreen : : EDITOR_SCRIPT ) ;
2014-05-06 17:41:19 +08:00
}
2014-02-09 22:10:30 -03:00
}
2014-02-20 23:01:44 -03:00
void EditorHelp : : _help_callback ( const String & p_topic ) {
2024-11-16 17:16:07 +01:00
String what = p_topic . get_slicec ( ' : ' , 0 ) ;
String clss = p_topic . get_slicec ( ' : ' , 1 ) ;
2014-02-20 23:01:44 -03:00
String name ;
2020-05-14 16:41:43 +02:00
if ( p_topic . get_slice_count ( " : " ) = = 3 ) {
2024-11-16 17:16:07 +01:00
name = p_topic . get_slicec ( ' : ' , 2 ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-20 23:01:44 -03:00
2022-04-26 17:57:36 +01:00
_request_help ( clss ) ; // First go to class.
2014-02-20 23:01:44 -03:00
int line = 0 ;
if ( what = = " class_desc " ) {
line = description_line ;
} else if ( what = = " class_signal " ) {
2020-05-14 16:41:43 +02:00
if ( signal_line . has ( name ) ) {
2014-02-20 23:01:44 -03:00
line = signal_line [ name ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-20 23:01:44 -03:00
} else if ( what = = " class_method " | | what = = " class_method_desc " ) {
2020-05-14 16:41:43 +02:00
if ( method_line . has ( name ) ) {
2014-02-20 23:01:44 -03:00
line = method_line [ name ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-20 23:01:44 -03:00
} else if ( what = = " class_property " ) {
2020-05-14 16:41:43 +02:00
if ( property_line . has ( name ) ) {
2014-02-20 23:01:44 -03:00
line = property_line [ name ] ;
2020-05-14 16:41:43 +02:00
}
2017-08-23 19:10:32 -03:00
} else if ( what = = " class_enum " ) {
2020-05-14 16:41:43 +02:00
if ( enum_line . has ( name ) ) {
2017-08-23 19:10:32 -03:00
line = enum_line [ name ] ;
2020-05-14 16:41:43 +02:00
}
2014-06-29 22:41:02 -03:00
} else if ( what = = " class_theme_item " ) {
2020-05-14 16:41:43 +02:00
if ( theme_property_line . has ( name ) ) {
2014-06-29 22:41:02 -03:00
line = theme_property_line [ name ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-20 23:01:44 -03:00
} else if ( what = = " class_constant " ) {
2020-05-14 16:41:43 +02:00
if ( constant_line . has ( name ) ) {
2014-02-20 23:01:44 -03:00
line = constant_line [ name ] ;
2020-05-14 16:41:43 +02:00
}
2022-07-04 18:56:34 +03:00
} else if ( what = = " class_annotation " ) {
if ( annotation_line . has ( name ) ) {
line = annotation_line [ name ] ;
}
2024-12-14 21:25:05 +03:00
} else if ( what = = " class_global " ) { // Deprecated.
2020-05-14 16:41:43 +02:00
if ( constant_line . has ( name ) ) {
2018-03-30 16:20:24 +02:00
line = constant_line [ name ] ;
2021-08-30 21:51:56 +02:00
} else if ( method_line . has ( name ) ) {
line = method_line [ name ] ;
2020-05-14 16:41:43 +02:00
} else {
2022-05-13 15:04:37 +02:00
HashMap < String , HashMap < String , int > > : : Iterator iter = enum_values_line . begin ( ) ;
2018-03-30 16:20:24 +02:00
while ( true ) {
2022-05-13 15:04:37 +02:00
if ( iter - > value . has ( name ) ) {
line = iter - > value [ name ] ;
2018-03-30 16:20:24 +02:00
break ;
2022-05-13 15:04:37 +02:00
} else if ( iter = = enum_values_line . last ( ) ) {
2018-03-30 16:20:24 +02:00
break ;
2020-05-14 16:41:43 +02:00
} else {
2022-05-13 15:04:37 +02:00
+ + iter ;
2020-05-14 16:41:43 +02:00
}
2018-03-30 16:20:24 +02:00
}
}
2014-02-20 23:01:44 -03:00
}
2024-07-04 16:55:51 +02:00
if ( class_desc - > is_finished ( ) ) {
2024-09-02 00:28:44 +02:00
class_desc - > scroll_to_paragraph ( line ) ;
2022-05-30 10:04:07 +03:00
} else {
scroll_to = line ;
}
2014-02-20 23:01:44 -03:00
}
2014-02-09 22:10:30 -03:00
2024-04-11 11:21:44 +03:00
static void _add_text_to_rt ( const String & p_bbcode , RichTextLabel * p_rt , const Control * p_owner_node , const String & p_class ) {
2024-03-08 22:16:55 +03:00
bool is_native = false ;
{
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * E = EditorHelp : : get_doc ( p_class ) ;
if ( E & & ! E - > is_script_doc ) {
2024-03-08 22:16:55 +03:00
is_native = true ;
}
}
2025-05-17 20:00:17 +02:00
const bool using_space_indent = int ( EDITOR_GET ( " text_editor/behavior/indent/type " ) ) = = 1 ;
const int indent_size = MAX ( 1 , int ( EDITOR_GET ( " text_editor/behavior/indent/size " ) ) ) ;
2016-08-23 23:15:16 -03:00
2024-03-08 22:16:55 +03:00
const Ref < Font > doc_font = p_owner_node - > get_theme_font ( SNAME ( " doc " ) , EditorStringName ( EditorFonts ) ) ;
const Ref < Font > doc_bold_font = p_owner_node - > get_theme_font ( SNAME ( " doc_bold " ) , EditorStringName ( EditorFonts ) ) ;
const Ref < Font > doc_italic_font = p_owner_node - > get_theme_font ( SNAME ( " doc_italic " ) , EditorStringName ( EditorFonts ) ) ;
const Ref < Font > doc_code_font = p_owner_node - > get_theme_font ( SNAME ( " doc_source " ) , EditorStringName ( EditorFonts ) ) ;
const Ref < Font > doc_kbd_font = p_owner_node - > get_theme_font ( SNAME ( " doc_keyboard " ) , EditorStringName ( EditorFonts ) ) ;
2019-10-31 18:47:12 +01:00
2024-11-20 20:08:07 +01:00
const int doc_font_size = p_owner_node - > get_theme_font_size ( SNAME ( " doc_size " ) , EditorStringName ( EditorFonts ) ) ;
2024-03-08 22:16:55 +03:00
const int doc_code_font_size = p_owner_node - > get_theme_font_size ( SNAME ( " doc_source_size " ) , EditorStringName ( EditorFonts ) ) ;
const int doc_kbd_font_size = p_owner_node - > get_theme_font_size ( SNAME ( " doc_keyboard_size " ) , EditorStringName ( EditorFonts ) ) ;
2023-02-11 17:33:38 +01:00
2022-10-31 19:51:48 +01:00
const Color type_color = p_owner_node - > get_theme_color ( SNAME ( " type_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color code_color = p_owner_node - > get_theme_color ( SNAME ( " code_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color kbd_color = p_owner_node - > get_theme_color ( SNAME ( " kbd_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color code_dark_color = Color ( code_color , 0.8 ) ;
const Color link_color = p_owner_node - > get_theme_color ( SNAME ( " link_color " ) , SNAME ( " EditorHelp " ) ) ;
2023-08-13 02:33:39 +02:00
const Color link_method_color = p_owner_node - > get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) ;
const Color link_property_color = link_color . lerp ( p_owner_node - > get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) , 0.25 ) ;
const Color link_annotation_color = link_color . lerp ( p_owner_node - > get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) , 0.5 ) ;
2022-10-31 19:51:48 +01:00
const Color code_bg_color = p_owner_node - > get_theme_color ( SNAME ( " code_bg_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color kbd_bg_color = p_owner_node - > get_theme_color ( SNAME ( " kbd_bg_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color param_bg_color = p_owner_node - > get_theme_color ( SNAME ( " param_bg_color " ) , SNAME ( " EditorHelp " ) ) ;
2016-05-29 11:37:26 -03:00
2025-05-17 20:00:17 +02:00
String bbcode = p_bbcode . dedent ( ) . remove_chars ( " \r " ) . strip_edges ( ) ;
2016-02-27 23:10:44 -03:00
2022-04-26 17:57:36 +01:00
// Select the correct code examples.
2020-07-22 22:41:10 +02:00
switch ( ( int ) EDITOR_GET ( " text_editor/help/class_reference_examples " ) ) {
case 0 : // GDScript
2024-03-08 22:16:55 +03:00
bbcode = bbcode . replace ( " [gdscript " , " [codeblock lang=gdscript " ) ; // Tag can have extra arguments.
2020-07-22 22:41:10 +02:00
bbcode = bbcode . replace ( " [/gdscript] " , " [/codeblock] " ) ;
2023-10-02 20:11:43 +02:00
for ( int pos = bbcode . find ( " [csharp " ) ; pos ! = - 1 ; pos = bbcode . find ( " [csharp " ) ) {
2021-11-11 09:08:08 +01:00
int end_pos = bbcode . find ( " [/csharp] " ) ;
if ( end_pos = = - 1 ) {
2020-07-22 22:41:10 +02:00
WARN_PRINT ( " Unclosed [csharp] block or parse fail in code (search for tag errors) " ) ;
break ;
}
2021-11-11 09:08:08 +01:00
bbcode = bbcode . left ( pos ) + bbcode . substr ( end_pos + 9 ) ; // 9 is length of "[/csharp]".
2020-07-22 22:41:10 +02:00
while ( bbcode [ pos ] = = ' \n ' ) {
2021-11-11 09:08:08 +01:00
bbcode = bbcode . left ( pos ) + bbcode . substr ( pos + 1 ) ;
2020-07-22 22:41:10 +02:00
}
}
break ;
case 1 : // C#
2024-03-08 22:16:55 +03:00
bbcode = bbcode . replace ( " [csharp " , " [codeblock lang=csharp " ) ; // Tag can have extra arguments.
2020-07-22 22:41:10 +02:00
bbcode = bbcode . replace ( " [/csharp] " , " [/codeblock] " ) ;
2023-10-02 20:11:43 +02:00
for ( int pos = bbcode . find ( " [gdscript " ) ; pos ! = - 1 ; pos = bbcode . find ( " [gdscript " ) ) {
2021-11-11 09:08:08 +01:00
int end_pos = bbcode . find ( " [/gdscript] " ) ;
if ( end_pos = = - 1 ) {
2020-07-22 22:41:10 +02:00
WARN_PRINT ( " Unclosed [gdscript] block or parse fail in code (search for tag errors) " ) ;
break ;
}
2021-11-11 09:08:08 +01:00
bbcode = bbcode . left ( pos ) + bbcode . substr ( end_pos + 11 ) ; // 11 is length of "[/gdscript]".
2020-07-22 22:41:10 +02:00
while ( bbcode [ pos ] = = ' \n ' ) {
2021-11-11 09:08:08 +01:00
bbcode = bbcode . left ( pos ) + bbcode . substr ( pos + 1 ) ;
2020-07-22 22:41:10 +02:00
}
}
break ;
case 2 : // GDScript and C#
2024-03-08 22:16:55 +03:00
bbcode = bbcode . replace ( " [csharp " , " [b]C#:[/b] \n [codeblock lang=csharp " ) ; // Tag can have extra arguments.
bbcode = bbcode . replace ( " [gdscript " , " [b]GDScript:[/b] \n [codeblock lang=gdscript " ) ; // Tag can have extra arguments.
2020-07-22 22:41:10 +02:00
bbcode = bbcode . replace ( " [/csharp] " , " [/codeblock] " ) ;
bbcode = bbcode . replace ( " [/gdscript] " , " [/codeblock] " ) ;
break ;
}
2022-04-26 17:57:36 +01:00
// Remove codeblocks (they would be printed otherwise).
2020-07-22 22:41:10 +02:00
bbcode = bbcode . replace ( " [codeblocks] \n " , " " ) ;
bbcode = bbcode . replace ( " \n [/codeblocks] " , " " ) ;
bbcode = bbcode . replace ( " [codeblocks] " , " " ) ;
bbcode = bbcode . replace ( " [/codeblocks] " , " " ) ;
2024-03-08 22:16:55 +03:00
// Remove `\n` here because `\n` is replaced by `\n\n` later.
// Will be compensated when parsing `[/codeblock]`.
2024-02-12 16:55:02 +03:00
bbcode = bbcode . replace ( " [/codeblock] \n " , " [/codeblock] " ) ;
2019-10-31 18:47:12 +01:00
2014-02-09 22:10:30 -03:00
List < String > tag_stack ;
2017-09-13 19:56:37 -05:00
int pos = 0 ;
2016-02-27 23:10:44 -03:00
while ( pos < bbcode . length ( ) ) {
2024-02-12 16:55:02 +03:00
int brk_pos = bbcode . find_char ( ' [ ' , pos ) ;
2014-02-09 22:10:30 -03:00
2020-05-14 16:41:43 +02:00
if ( brk_pos < 0 ) {
2016-02-27 23:10:44 -03:00
brk_pos = bbcode . length ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
if ( brk_pos > pos ) {
2024-03-08 22:16:55 +03:00
p_rt - > add_text ( bbcode . substr ( pos , brk_pos - pos ) . replace ( " \n " , " \n \n " ) ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 16:41:43 +02:00
if ( brk_pos = = bbcode . length ( ) ) {
2022-04-26 17:57:36 +01:00
break ; // Nothing else to add.
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2024-02-12 16:55:02 +03:00
int brk_end = bbcode . find_char ( ' ] ' , brk_pos + 1 ) ;
2014-02-09 22:10:30 -03:00
if ( brk_end = = - 1 ) {
2025-02-26 11:41:11 +01:00
p_rt - > add_text ( bbcode . substr ( brk_pos ) . replace ( " \n " , " \n \n " ) ) ;
2014-02-09 22:10:30 -03:00
break ;
}
2024-03-08 22:16:55 +03:00
const String tag = bbcode . substr ( brk_pos + 1 , brk_end - brk_pos - 1 ) ;
2014-02-09 22:10:30 -03:00
if ( tag . begins_with ( " / " ) ) {
2024-04-11 11:21:44 +03:00
bool tag_ok = tag_stack . size ( ) & & tag_stack . front ( ) - > get ( ) = = tag . substr ( 1 ) ;
2017-09-13 19:56:37 -05:00
2014-02-09 22:10:30 -03:00
if ( ! tag_ok ) {
2016-08-23 23:15:16 -03:00
p_rt - > add_text ( " [ " ) ;
2017-11-11 09:11:24 +08:00
pos = brk_pos + 1 ;
2014-02-09 22:10:30 -03:00
continue ;
}
tag_stack . pop_front ( ) ;
pos = brk_end + 1 ;
2024-11-20 20:08:07 +01:00
if ( tag = = " /img " ) {
// Nothing to do.
} else if ( tag = = " /url " ) {
p_rt - > pop ( ) ; // meta
p_rt - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
p_rt - > add_text ( nbsp ) ;
2024-11-20 20:08:07 +01:00
p_rt - > add_image ( p_owner_node - > get_editor_theme_icon ( SNAME ( " ExternalLink " ) ) , 0 , doc_font_size , link_color ) ;
} else {
2024-03-08 22:16:55 +03:00
p_rt - > pop ( ) ;
2024-01-19 09:56:42 +01:00
}
2023-10-02 20:11:43 +02:00
} else if ( tag . begins_with ( " method " ) | | tag . begins_with ( " constructor " ) | | tag . begins_with ( " operator " ) | | 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 " ) ) {
2024-02-12 16:55:02 +03:00
const int tag_end = tag . find_char ( ' ' ) ;
2024-04-11 11:21:44 +03:00
const String link_tag = tag . left ( tag_end ) ;
const String link_target = tag . substr ( tag_end + 1 ) . lstrip ( " " ) ;
2019-04-27 21:15:07 +02:00
2022-10-31 19:51:48 +01:00
Color target_color = link_color ;
2024-03-01 16:40:00 +01:00
RichTextLabel : : MetaUnderline underline_mode = RichTextLabel : : META_UNDERLINE_ON_HOVER ;
2023-10-02 20:11:43 +02:00
if ( link_tag = = " method " | | link_tag = = " constructor " | | link_tag = = " operator " ) {
2022-10-31 19:51:48 +01:00
target_color = link_method_color ;
2024-02-12 16:55:02 +03:00
} else if ( link_tag = = " member " | | link_tag = = " signal " | | link_tag = = " theme_item " ) {
2022-10-31 19:51:48 +01:00
target_color = link_property_color ;
} else if ( link_tag = = " annotation " ) {
target_color = link_annotation_color ;
2024-03-01 16:40:00 +01:00
} else {
// Better visibility for constants, enums, etc.
underline_mode = RichTextLabel : : META_UNDERLINE_ALWAYS ;
2022-10-31 19:51:48 +01:00
}
2024-03-01 23:53:33 +01:00
2024-03-08 22:16:55 +03:00
// Use monospace font to make clickable references
// easier to distinguish from inline code and other text.
p_rt - > push_font ( doc_code_font ) ;
p_rt - > push_font_size ( doc_code_font_size ) ;
2022-10-31 19:51:48 +01:00
p_rt - > push_color ( target_color ) ;
2024-03-01 16:40:00 +01:00
p_rt - > push_meta ( " @ " + link_tag + " " + link_target , underline_mode ) ;
2024-03-01 23:53:33 +01:00
if ( link_tag = = " member " & &
2024-12-05 17:56:08 +01:00
( ( ! link_target . contains_char ( ' . ' ) & & ( p_class = = " ProjectSettings " | | p_class = = " EditorSettings " ) ) | |
2024-03-01 23:53:33 +01:00
link_target . begins_with ( " ProjectSettings. " ) | | link_target . begins_with ( " EditorSettings. " ) ) ) {
// Special formatting for both ProjectSettings and EditorSettings.
String prefix ;
if ( link_target . begins_with ( " EditorSettings. " ) ) {
prefix = " ( " + TTR ( " Editor " ) + " ) " ;
}
const String setting_name = link_target . trim_prefix ( " ProjectSettings. " ) . trim_prefix ( " EditorSettings. " ) ;
PackedStringArray setting_sections ;
for ( const String & section : setting_name . split ( " / " , false ) ) {
setting_sections . append ( EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( section , EditorPropertyNameProcessor : : get_settings_style ( ) ) ) ;
}
p_rt - > push_bold ( ) ;
p_rt - > add_text ( prefix + String ( " > " ) . join ( setting_sections ) ) ;
p_rt - > pop ( ) ; // bold
} else {
p_rt - > add_text ( link_target + ( link_tag = = " method " ? " () " : " " ) ) ;
}
p_rt - > pop ( ) ; // meta
p_rt - > pop ( ) ; // color
2024-03-08 22:16:55 +03:00
p_rt - > pop ( ) ; // font_size
2023-02-11 17:33:38 +01:00
p_rt - > pop ( ) ; // font
2014-02-09 22:10:30 -03:00
2024-03-08 22:16:55 +03:00
pos = brk_end + 1 ;
2022-08-06 20:48:54 +03:00
} else if ( tag . begins_with ( " param " ) ) {
2024-02-12 16:55:02 +03:00
const int tag_end = tag . find_char ( ' ' ) ;
2024-04-11 11:21:44 +03:00
const String param_name = tag . substr ( tag_end + 1 ) . lstrip ( " " ) ;
2022-08-06 20:48:54 +03:00
// Use monospace font with translucent background color to make code easier to distinguish from other text.
p_rt - > push_font ( doc_code_font ) ;
2023-02-11 17:33:38 +01:00
p_rt - > push_font_size ( doc_code_font_size ) ;
2022-10-31 19:51:48 +01:00
p_rt - > push_bgcolor ( param_bg_color ) ;
2022-08-06 20:48:54 +03:00
p_rt - > push_color ( code_color ) ;
2024-03-08 22:16:55 +03:00
2022-08-06 20:48:54 +03:00
p_rt - > add_text ( param_name ) ;
2024-03-08 22:16:55 +03:00
p_rt - > pop ( ) ; // color
p_rt - > pop ( ) ; // bgcolor
p_rt - > pop ( ) ; // font_size
2023-02-11 17:33:38 +01:00
p_rt - > pop ( ) ; // font
2022-08-06 20:48:54 +03:00
2024-03-08 22:16:55 +03:00
pos = brk_end + 1 ;
2023-06-24 18:09:31 +02:00
} else if ( tag = = p_class ) {
// Use a bold font when class reference tags are in their own page.
p_rt - > push_font ( doc_bold_font ) ;
p_rt - > add_text ( tag ) ;
2024-03-08 22:16:55 +03:00
p_rt - > pop ( ) ; // font
2023-06-24 18:09:31 +02:00
pos = brk_end + 1 ;
2024-08-19 17:03:03 -04:00
} else if ( EditorHelp : : has_doc ( tag ) ) {
2023-06-24 18:09:31 +02:00
// Use a monospace font for class reference tags such as [Node2D] or [SceneTree].
2020-01-24 13:29:19 +01:00
p_rt - > push_font ( doc_code_font ) ;
2023-02-11 17:33:38 +01:00
p_rt - > push_font_size ( doc_code_font_size ) ;
2022-10-31 19:51:48 +01:00
p_rt - > push_color ( type_color ) ;
2024-03-01 16:40:00 +01:00
p_rt - > push_meta ( " # " + tag , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
2024-03-08 22:16:55 +03:00
2016-08-23 23:15:16 -03:00
p_rt - > add_text ( tag ) ;
2023-06-24 18:09:31 +02:00
2024-03-08 22:16:55 +03:00
p_rt - > pop ( ) ; // meta
p_rt - > pop ( ) ; // color
p_rt - > pop ( ) ; // font_size
p_rt - > pop ( ) ; // font
2023-02-11 17:33:38 +01:00
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
} else if ( tag = = " b " ) {
2022-04-26 17:57:36 +01:00
// Use bold font.
2019-06-15 18:18:32 +02:00
p_rt - > push_font ( doc_bold_font ) ;
2023-02-11 17:33:38 +01:00
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " i " ) {
2022-04-26 17:57:36 +01:00
// Use italics font.
2022-03-11 16:22:04 +02:00
p_rt - > push_font ( doc_italic_font ) ;
2023-02-11 17:33:38 +01:00
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
2023-10-02 20:11:43 +02:00
} else if ( tag = = " code " | | tag . begins_with ( " code " ) ) {
2024-03-08 22:16:55 +03:00
int end_pos = bbcode . find ( " [/code] " , brk_end + 1 ) ;
if ( end_pos < 0 ) {
end_pos = bbcode . length ( ) ;
}
2022-10-31 19:51:48 +01:00
// Use monospace font with darkened background color to make code easier to distinguish from other text.
2016-08-23 23:15:16 -03:00
p_rt - > push_font ( doc_code_font ) ;
2023-02-11 17:33:38 +01:00
p_rt - > push_font_size ( doc_code_font_size ) ;
2022-10-31 19:51:48 +01:00
p_rt - > push_bgcolor ( code_bg_color ) ;
2023-08-13 02:33:39 +02:00
p_rt - > push_color ( code_color . lerp ( p_owner_node - > get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) , 0.6 ) ) ;
2023-02-11 17:33:38 +01:00
2024-03-08 22:16:55 +03:00
p_rt - > add_text ( bbcode . substr ( brk_end + 1 , end_pos - ( brk_end + 1 ) ) ) ;
p_rt - > pop ( ) ; // color
p_rt - > pop ( ) ; // bgcolor
p_rt - > pop ( ) ; // font_size
p_rt - > pop ( ) ; // font
pos = end_pos + 7 ; // `len("[/code]")`.
2023-10-02 20:11:43 +02:00
} else if ( tag = = " codeblock " | | tag . begins_with ( " codeblock " ) ) {
2024-03-08 22:16:55 +03:00
int end_pos = bbcode . find ( " [/codeblock] " , brk_end + 1 ) ;
if ( end_pos < 0 ) {
end_pos = bbcode . length ( ) ;
}
const String codeblock_text = bbcode . substr ( brk_end + 1 , end_pos - ( brk_end + 1 ) ) . strip_edges ( ) ;
String codeblock_copy_text = codeblock_text ;
2025-05-17 20:00:17 +02:00
if ( using_space_indent ) {
// Replace the code block's tab indentation with spaces.
2024-03-08 22:16:55 +03:00
StringBuilder builder ;
PackedStringArray text_lines = codeblock_copy_text . split ( " \n " ) ;
for ( const String & line : text_lines ) {
const String stripped_line = line . dedent ( ) ;
2025-05-17 20:00:17 +02:00
const int tab_count = line . length ( ) - stripped_line . length ( ) ;
2024-03-08 22:16:55 +03:00
if ( builder . num_strings_appended ( ) > 0 ) {
builder . append ( " \n " ) ;
}
2025-05-17 20:00:17 +02:00
if ( tab_count > 0 ) {
builder . append ( String ( " " ) . repeat ( tab_count * indent_size ) + stripped_line ) ;
2024-03-08 22:16:55 +03:00
} else {
builder . append ( line ) ;
}
}
codeblock_copy_text = builder . as_string ( ) ;
}
String lang ;
const PackedStringArray args = tag . trim_prefix ( " codeblock " ) . split ( " " , false ) ;
for ( int i = args . size ( ) - 1 ; i > = 0 ; i - - ) {
if ( args [ i ] . begins_with ( " lang= " ) ) {
lang = args [ i ] . trim_prefix ( " lang= " ) ;
break ;
}
}
2022-10-31 19:51:48 +01:00
// Use monospace font with darkened background color to make code easier to distinguish from other text.
2022-07-04 17:14:50 +02:00
// 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 ) ;
2023-02-11 17:33:38 +01:00
p_rt - > push_font_size ( doc_code_font_size ) ;
2024-01-19 09:56:42 +01:00
p_rt - > push_table ( 2 ) ;
2024-03-08 22:16:55 +03:00
2022-07-04 17:14:50 +02:00
p_rt - > push_cell ( ) ;
2022-10-31 19:51:48 +01:00
p_rt - > set_cell_row_background_color ( code_bg_color , Color ( code_bg_color , 0.99 ) ) ;
2022-07-17 22:55:20 +02:00
p_rt - > set_cell_padding ( Rect2 ( 10 * EDSCALE , 10 * EDSCALE , 10 * EDSCALE , 10 * EDSCALE ) ) ;
2022-10-31 19:51:48 +01:00
p_rt - > push_color ( code_dark_color ) ;
2023-02-11 17:33:38 +01:00
2024-03-09 20:51:30 +01:00
bool codeblock_printed = false ;
2024-03-08 22:16:55 +03:00
# ifdef MODULE_GDSCRIPT_ENABLED
2024-03-09 20:51:30 +01:00
if ( ! codeblock_printed & & ( lang . is_empty ( ) | | lang = = " gdscript " ) ) {
2024-03-08 22:16:55 +03:00
EditorHelpHighlighter : : get_singleton ( ) - > highlight ( p_rt , EditorHelpHighlighter : : LANGUAGE_GDSCRIPT , codeblock_text , is_native ) ;
2024-03-09 20:51:30 +01:00
codeblock_printed = true ;
}
2024-03-08 22:16:55 +03:00
# endif
2024-03-09 20:51:30 +01:00
2024-03-08 22:16:55 +03:00
# ifdef MODULE_MONO_ENABLED
2024-03-09 20:51:30 +01:00
if ( ! codeblock_printed & & lang = = " csharp " ) {
2024-03-08 22:16:55 +03:00
EditorHelpHighlighter : : get_singleton ( ) - > highlight ( p_rt , EditorHelpHighlighter : : LANGUAGE_CSHARP , codeblock_text , is_native ) ;
2024-03-09 20:51:30 +01:00
codeblock_printed = true ;
}
2024-03-08 22:16:55 +03:00
# endif
2024-03-09 20:51:30 +01:00
if ( ! codeblock_printed ) {
2024-03-08 22:16:55 +03:00
p_rt - > add_text ( codeblock_text ) ;
2024-03-09 20:51:30 +01:00
codeblock_printed = true ;
2024-03-08 22:16:55 +03:00
}
p_rt - > pop ( ) ; // color
p_rt - > pop ( ) ; // cell
// Copy codeblock button.
p_rt - > push_cell ( ) ;
p_rt - > set_cell_row_background_color ( code_bg_color , Color ( code_bg_color , 0.99 ) ) ;
p_rt - > set_cell_padding ( Rect2 ( 0 , 10 * EDSCALE , 0 , 10 * EDSCALE ) ) ;
p_rt - > set_cell_size_override ( Vector2 ( 1 , 1 ) , Vector2 ( 10 , 10 ) * EDSCALE ) ;
p_rt - > push_meta ( " ^ " + codeblock_copy_text , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
p_rt - > add_image ( p_owner_node - > get_editor_theme_icon ( SNAME ( " ActionCopy " ) ) , 24 * EDSCALE , 24 * EDSCALE , Color ( link_property_color , 0.3 ) , INLINE_ALIGNMENT_BOTTOM_TO , Rect2 ( ) , Variant ( ) , false , TTR ( " Click to copy. " ) ) ;
p_rt - > pop ( ) ; // meta
p_rt - > pop ( ) ; // cell
p_rt - > pop ( ) ; // table
p_rt - > pop ( ) ; // font_size
p_rt - > pop ( ) ; // font
pos = end_pos + 12 ; // `len("[/codeblock]")`.
// Compensate for `\n` removed before the loop.
if ( pos < bbcode . length ( ) ) {
p_rt - > add_newline ( ) ;
}
2020-03-10 13:41:36 +03:00
} else if ( tag = = " kbd " ) {
2024-03-08 22:16:55 +03:00
int end_pos = bbcode . find ( " [/kbd] " , brk_end + 1 ) ;
if ( end_pos < 0 ) {
end_pos = bbcode . length ( ) ;
}
2022-07-04 17:14:50 +02:00
// Use keyboard font with custom color and background color.
2020-03-10 13:41:36 +03:00
p_rt - > push_font ( doc_kbd_font ) ;
2023-02-11 17:33:38 +01:00
p_rt - > push_font_size ( doc_kbd_font_size ) ;
2022-10-31 19:51:48 +01:00
p_rt - > push_bgcolor ( kbd_bg_color ) ;
2020-03-10 13:41:36 +03:00
p_rt - > push_color ( kbd_color ) ;
2023-02-11 17:33:38 +01:00
2024-03-08 22:16:55 +03:00
p_rt - > add_text ( bbcode . substr ( brk_end + 1 , end_pos - ( brk_end + 1 ) ) ) ;
p_rt - > pop ( ) ; // color
p_rt - > pop ( ) ; // bgcolor
p_rt - > pop ( ) ; // font_size
p_rt - > pop ( ) ; // font
2023-02-11 17:33:38 +01:00
2024-03-08 22:16:55 +03:00
pos = end_pos + 6 ; // `len("[/kbd]")`.
2014-02-09 22:10:30 -03:00
} else if ( tag = = " center " ) {
2022-04-26 17:57:36 +01:00
// Align to center.
2021-11-24 20:58:47 -06:00
p_rt - > push_paragraph ( HORIZONTAL_ALIGNMENT_CENTER , Control : : TEXT_DIRECTION_AUTO , " " ) ;
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " br " ) {
2022-04-26 17:57:36 +01:00
// Force a line break.
2016-08-23 23:15:16 -03:00
p_rt - > add_newline ( ) ;
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
} else if ( tag = = " u " ) {
2022-04-26 17:57:36 +01:00
// Use underline.
2016-08-23 23:15:16 -03:00
p_rt - > push_underline ( ) ;
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
} else if ( tag = = " s " ) {
2022-04-26 17:57:36 +01:00
// Use strikethrough.
2019-06-15 18:18:32 +02:00
p_rt - > push_strikethrough ( ) ;
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
2023-10-10 11:30:36 +03:00
} else if ( tag = = " lb " ) {
p_rt - > add_text ( " [ " ) ;
pos = brk_end + 1 ;
} else if ( tag = = " rb " ) {
p_rt - > add_text ( " ] " ) ;
pos = brk_end + 1 ;
2024-11-20 20:08:07 +01:00
} else if ( tag = = " url " | | tag . begins_with ( " url= " ) ) {
String url ;
if ( tag . begins_with ( " url= " ) ) {
url = tag . substr ( 4 ) ;
} else {
int end = bbcode . find_char ( ' [ ' , brk_end ) ;
if ( end = = - 1 ) {
end = bbcode . length ( ) ;
}
url = bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2024-11-20 20:08:07 +01:00
p_rt - > push_color ( link_color ) ;
p_rt - > push_meta ( url , RichTextLabel : : META_UNDERLINE_ON_HOVER , url + " \n \n " + TTR ( " Click to open in browser. " ) ) ;
2023-02-11 17:33:38 +01:00
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( " url " ) ;
2022-12-07 22:40:03 +02:00
} else if ( tag . begins_with ( " img " ) ) {
int width = 0 ;
int height = 0 ;
bool size_in_percent = false ;
if ( tag . length ( ) > 4 ) {
2024-04-11 11:21:44 +03:00
Vector < String > subtags = tag . substr ( 4 ) . split ( " " ) ;
2022-12-07 22:40:03 +02:00
HashMap < String , String > bbcode_options ;
for ( int i = 0 ; i < subtags . size ( ) ; i + + ) {
const String & expr = subtags [ i ] ;
2024-02-12 16:55:02 +03:00
int value_pos = expr . find_char ( ' = ' ) ;
2022-12-07 22:40:03 +02:00
if ( value_pos > - 1 ) {
2024-04-11 11:21:44 +03:00
bbcode_options [ expr . left ( value_pos ) ] = expr . substr ( value_pos + 1 ) . unquote ( ) ;
2022-12-07 22:40:03 +02:00
}
}
HashMap < String , String > : : Iterator width_option = bbcode_options . find ( " width " ) ;
if ( width_option ) {
width = width_option - > value . to_int ( ) ;
if ( width_option - > value . ends_with ( " % " ) ) {
size_in_percent = true ;
}
}
HashMap < String , String > : : Iterator height_option = bbcode_options . find ( " height " ) ;
if ( height_option ) {
height = height_option - > value . to_int ( ) ;
if ( height_option - > value . ends_with ( " % " ) ) {
size_in_percent = true ;
}
}
}
2024-02-12 16:55:02 +03:00
int end = bbcode . find_char ( ' [ ' , brk_end ) ;
2020-05-14 16:41:43 +02:00
if ( end = = - 1 ) {
2016-02-27 23:10:44 -03:00
end = bbcode . length ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2024-03-08 22:16:55 +03:00
String image_path = bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
p_rt - > add_image ( ResourceLoader : : load ( image_path , " Texture2D " ) , width , height , Color ( 1 , 1 , 1 ) , INLINE_ALIGNMENT_CENTER , Rect2 ( ) , Variant ( ) , false , String ( ) , size_in_percent ) ;
2014-02-09 22:10:30 -03:00
pos = end ;
2022-12-07 22:40:03 +02:00
tag_stack . push_front ( " img " ) ;
2014-02-09 22:10:30 -03:00
} else if ( tag . begins_with ( " color= " ) ) {
2024-04-11 11:21:44 +03:00
String col = tag . substr ( 6 ) ;
2020-11-16 16:31:58 +03:00
Color color = Color : : from_string ( col , Color ( ) ) ;
2016-08-23 23:15:16 -03:00
p_rt - > push_color ( color ) ;
2023-02-11 17:33:38 +01:00
2014-02-09 22:10:30 -03:00
pos = brk_end + 1 ;
tag_stack . push_front ( " color " ) ;
} else if ( tag . begins_with ( " font= " ) ) {
2024-04-11 11:21:44 +03:00
String font_path = tag . substr ( 5 ) ;
2024-03-08 22:16:55 +03:00
Ref < Font > font = ResourceLoader : : load ( font_path , " Font " ) ;
2020-05-14 16:41:43 +02:00
if ( font . is_valid ( ) ) {
2016-08-23 23:15:16 -03:00
p_rt - > push_font ( font ) ;
2020-05-14 16:41:43 +02:00
} else {
2016-08-23 23:15:16 -03:00
p_rt - > push_font ( doc_font ) ;
2014-02-09 22:10:30 -03:00
}
pos = brk_end + 1 ;
tag_stack . push_front ( " font " ) ;
} else {
2024-03-08 22:16:55 +03:00
p_rt - > add_text ( " [ " ) ; // Ignore.
2014-02-09 22:10:30 -03:00
pos = brk_pos + 1 ;
}
}
2024-03-08 22:16:55 +03:00
// Close unclosed tags.
for ( const String & tag : tag_stack ) {
if ( tag ! = " img " ) {
p_rt - > pop ( ) ;
}
}
2016-08-23 23:15:16 -03:00
}
void EditorHelp : : _add_text ( const String & p_bbcode ) {
2023-06-24 18:09:31 +02:00
_add_text_to_rt ( p_bbcode , class_desc , this , edited_class ) ;
2014-02-09 22:10:30 -03:00
}
2024-08-19 17:03:03 -04:00
void EditorHelp : : _wait_for_thread ( Thread & p_thread ) {
if ( p_thread . is_started ( ) ) {
p_thread . wait_to_finish ( ) ;
2021-07-25 14:44:03 -03:00
}
}
2023-10-20 22:14:04 +02:00
void EditorHelp : : _compute_doc_version_hash ( ) {
uint32_t version_hash = Engine : : get_singleton ( ) - > get_version_info ( ) . hash ( ) ;
doc_version_hash = vformat ( " %d/%d/%d/%s " , version_hash , ClassDB : : get_api_hash ( ClassDB : : API_CORE ) , ClassDB : : get_api_hash ( ClassDB : : API_EDITOR ) , _doc_data_hash ) ;
2023-02-07 21:14:00 +01:00
}
2023-10-20 22:14:04 +02:00
String EditorHelp : : get_cache_full_path ( ) {
2025-03-03 22:27:29 -08:00
return EditorPaths : : get_singleton ( ) - > get_cache_dir ( ) . path_join ( vformat ( " editor_doc_cache-%d.%d.res " , GODOT_VERSION_MAJOR , GODOT_VERSION_MINOR ) ) ;
2023-04-25 11:40:56 +02:00
}
2023-02-07 21:14:00 +01:00
2024-08-19 17:03:03 -04:00
String EditorHelp : : get_script_doc_cache_full_path ( ) {
return EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " editor_script_doc_cache.res " ) ;
}
DocTools * EditorHelp : : get_doc_data ( ) {
_wait_for_thread ( ) ;
return doc ;
}
bool EditorHelp : : has_doc ( const String & p_class_name ) {
return get_doc ( p_class_name ) ! = nullptr ;
}
DocData : : ClassDoc * EditorHelp : : get_doc ( const String & p_class_name ) {
return get_doc_data ( ) - > class_list . getptr ( p_class_name ) ;
}
void EditorHelp : : add_doc ( const DocData : : ClassDoc & p_class_doc ) {
if ( ! _script_docs_loaded . is_set ( ) ) {
_docs_to_add . push_back ( p_class_doc ) ;
return ;
}
get_doc_data ( ) - > add_doc ( p_class_doc ) ;
}
void EditorHelp : : remove_doc ( const String & p_class_name ) {
if ( ! _script_docs_loaded . is_set ( ) ) {
_docs_to_remove . push_back ( p_class_name ) ;
return ;
}
DocTools * dt = get_doc_data ( ) ;
if ( dt - > has_doc ( p_class_name ) ) {
dt - > remove_doc ( p_class_name ) ;
}
}
void EditorHelp : : remove_script_doc_by_path ( const String & p_path ) {
if ( ! _script_docs_loaded . is_set ( ) ) {
_docs_to_remove_by_path . push_back ( p_path ) ;
return ;
}
get_doc_data ( ) - > remove_script_doc_by_path ( p_path ) ;
}
2023-10-19 00:50:30 +02:00
void EditorHelp : : load_xml_buffer ( const uint8_t * p_buffer , int p_size ) {
if ( ! ext_doc ) {
ext_doc = memnew ( DocTools ) ;
}
ext_doc - > load_xml ( p_buffer , p_size ) ;
if ( doc ) {
doc - > load_xml ( p_buffer , p_size ) ;
}
}
void EditorHelp : : remove_class ( const String & p_class ) {
if ( ext_doc & & ext_doc - > has_doc ( p_class ) ) {
ext_doc - > remove_doc ( p_class ) ;
}
if ( doc & & doc - > has_doc ( p_class ) ) {
2024-08-19 17:03:03 -04:00
remove_doc ( p_class ) ;
2023-10-19 00:50:30 +02:00
}
}
2023-02-07 21:14:00 +01:00
void EditorHelp : : _load_doc_thread ( void * p_udata ) {
2024-08-19 17:03:03 -04:00
bool use_script_cache = ( bool ) p_udata ;
2023-06-23 16:39:51 +02:00
Ref < Resource > cache_res = ResourceLoader : : load ( get_cache_full_path ( ) ) ;
2023-10-20 22:14:04 +02:00
if ( cache_res . is_valid ( ) & & cache_res - > get_meta ( " version_hash " , " " ) = = doc_version_hash ) {
2023-06-23 16:39:51 +02:00
Array classes = cache_res - > get_meta ( " classes " , Array ( ) ) ;
for ( int i = 0 ; i < classes . size ( ) ; i + + ) {
doc - > add_doc ( DocData : : ClassDoc : : from_dict ( classes [ i ] ) ) ;
2023-02-07 21:14:00 +01:00
}
2024-08-19 17:03:03 -04:00
if ( use_script_cache ) {
callable_mp_static ( & EditorHelp : : load_script_doc_cache ) . call_deferred ( ) ;
}
2023-10-31 18:27:17 +01:00
// Extensions' docs are not cached. Generate them now (on the main thread).
callable_mp_static ( & EditorHelp : : _gen_extensions_docs ) . call_deferred ( ) ;
2023-02-07 21:14:00 +01:00
} else {
2023-10-31 18:27:17 +01:00
// We have to go back to the main thread to start from scratch, bypassing any possibly existing cache.
2024-08-19 17:03:03 -04:00
callable_mp_static ( & EditorHelp : : generate_doc ) . call_deferred ( false , use_script_cache ) ;
2023-02-07 21:14:00 +01:00
}
2023-12-08 12:52:49 +01:00
OS : : get_singleton ( ) - > benchmark_end_measure ( " EditorHelp " , vformat ( " Generate Documentation (Run %d) " , doc_generation_count ) ) ;
2023-02-07 21:14:00 +01:00
}
2021-07-25 14:44:03 -03:00
void EditorHelp : : _gen_doc_thread ( void * p_udata ) {
2020-11-29 09:12:06 +05:30
DocTools compdoc ;
2015-11-17 09:46:08 -03:00
compdoc . load_compressed ( _doc_data_compressed , _doc_data_compressed_size , _doc_data_uncompressed_size ) ;
2022-04-26 17:57:36 +01:00
doc - > merge_from ( compdoc ) ; // Ensure all is up to date.
2023-02-07 21:14:00 +01:00
2023-06-23 16:39:51 +02:00
Ref < Resource > cache_res ;
2023-02-07 21:14:00 +01:00
cache_res . instantiate ( ) ;
2023-10-20 22:14:04 +02:00
cache_res - > set_meta ( " version_hash " , doc_version_hash ) ;
2023-02-07 21:14:00 +01:00
Array classes ;
for ( const KeyValue < String , DocData : : ClassDoc > & E : doc - > class_list ) {
2023-10-31 18:27:17 +01:00
if ( ClassDB : : class_exists ( E . value . name ) ) {
ClassDB : : APIType api = ClassDB : : get_api_type ( E . value . name ) ;
if ( api = = ClassDB : : API_EXTENSION | | api = = ClassDB : : API_EDITOR_EXTENSION ) {
continue ;
}
}
2023-02-07 21:14:00 +01:00
classes . push_back ( DocData : : ClassDoc : : to_dict ( E . value ) ) ;
}
2023-06-23 16:39:51 +02:00
cache_res - > set_meta ( " classes " , classes ) ;
2023-02-07 21:14:00 +01:00
Error err = ResourceSaver : : save ( cache_res , get_cache_full_path ( ) , ResourceSaver : : FLAG_COMPRESS ) ;
if ( err ) {
ERR_PRINT ( " Cannot save editor help cache ( " + get_cache_full_path ( ) + " ). " ) ;
}
2023-12-08 12:52:49 +01:00
2024-08-19 17:03:03 -04:00
// Load script docs after native ones are cached so native cache doesn't contain script docs.
bool use_script_cache = ( bool ) p_udata ;
if ( use_script_cache ) {
callable_mp_static ( & EditorHelp : : load_script_doc_cache ) . call_deferred ( ) ;
}
2023-12-08 12:52:49 +01:00
OS : : get_singleton ( ) - > benchmark_end_measure ( " EditorHelp " , vformat ( " Generate Documentation (Run %d) " , doc_generation_count ) ) ;
2014-02-09 22:10:30 -03:00
}
2023-10-31 18:27:17 +01:00
void EditorHelp : : _gen_extensions_docs ( ) {
doc - > generate ( ( DocTools : : GENERATE_FLAG_SKIP_BASIC_TYPES | DocTools : : GENERATE_FLAG_EXTENSION_CLASSES_ONLY ) ) ;
2023-10-19 00:50:30 +02:00
// Append extra doc data, as it gets overridden by the generation step.
if ( ext_doc ) {
doc - > merge_from ( * ext_doc ) ;
}
2023-10-31 18:27:17 +01:00
}
2024-08-19 17:03:03 -04:00
static void _load_script_doc_cache ( bool p_changes ) {
EditorHelp : : load_script_doc_cache ( ) ;
}
void EditorHelp : : load_script_doc_cache ( ) {
if ( ! ProjectSettings : : get_singleton ( ) - > is_project_loaded ( ) ) {
print_verbose ( " Skipping loading script doc cache since no project is open. " ) ;
return ;
}
_wait_for_thread ( ) ;
if ( ! ResourceLoader : : exists ( get_script_doc_cache_full_path ( ) ) ) {
print_verbose ( " Script documentation cache not found. Regenerating it may take a while for projects with many scripts. " ) ;
regenerate_script_doc_cache ( ) ;
return ;
}
if ( EditorFileSystem : : get_singleton ( ) - > is_scanning ( ) ) {
// This is assuming EditorFileSystem is performing first scan. We must wait until it is done.
EditorFileSystem : : get_singleton ( ) - > connect ( SNAME ( " sources_changed " ) , callable_mp_static ( _load_script_doc_cache ) , CONNECT_ONE_SHOT ) ;
return ;
}
worker_thread . start ( _load_script_doc_cache_thread , nullptr ) ;
}
void EditorHelp : : _process_postponed_docs ( ) {
for ( const String & class_name : _docs_to_remove ) {
doc - > remove_doc ( class_name ) ;
}
for ( const String & path : _docs_to_remove_by_path ) {
doc - > remove_script_doc_by_path ( path ) ;
}
for ( const DocData : : ClassDoc & cd : _docs_to_add ) {
doc - > add_doc ( cd ) ;
}
_docs_to_add . clear ( ) ;
_docs_to_remove . clear ( ) ;
_docs_to_remove_by_path . clear ( ) ;
}
void EditorHelp : : _load_script_doc_cache_thread ( void * p_udata ) {
ERR_FAIL_COND_MSG ( ! ProjectSettings : : get_singleton ( ) - > is_project_loaded ( ) , " Error: cannot load script doc cache without a project. " ) ;
ERR_FAIL_COND_MSG ( ! ResourceLoader : : exists ( get_script_doc_cache_full_path ( ) ) , " Error: cannot load script doc cache from inexistent file. " ) ;
Ref < Resource > script_doc_cache_res = ResourceLoader : : load ( get_script_doc_cache_full_path ( ) , " " , ResourceFormatLoader : : CACHE_MODE_IGNORE ) ;
if ( script_doc_cache_res . is_null ( ) ) {
print_verbose ( " Script doc cache is corrupted. Regenerating it instead. " ) ;
_delete_script_doc_cache ( ) ;
callable_mp_static ( EditorHelp : : regenerate_script_doc_cache ) . call_deferred ( ) ;
return ;
}
Array classes = script_doc_cache_res - > get_meta ( " classes " , Array ( ) ) ;
for ( const Dictionary dict : classes ) {
doc - > add_doc ( DocData : : ClassDoc : : from_dict ( dict ) ) ;
}
2023-10-31 18:27:17 +01:00
2024-08-19 17:03:03 -04:00
// Protect from race condition in other threads reading / this thread writing to _docs_to_add/remove/etc.
_script_docs_loaded . set ( ) ;
// Deal with docs likely added from EditorFileSystem's scans while the cache was loading in EditorHelp::worker_thread.
_process_postponed_docs ( ) ;
// Always delete the doc cache after successful load since most uses of editor will change a script, invalidating cache.
_delete_script_doc_cache ( ) ;
}
// Helper method to deal with "sources_changed" signal having a parameter.
static void _regenerate_script_doc_cache ( bool p_changes ) {
EditorHelp : : regenerate_script_doc_cache ( ) ;
}
void EditorHelp : : regenerate_script_doc_cache ( ) {
if ( EditorFileSystem : : get_singleton ( ) - > is_scanning ( ) ) {
// Wait until EditorFileSystem scanning is complete to use updated filesystem structure.
EditorFileSystem : : get_singleton ( ) - > connect ( SNAME ( " sources_changed " ) , callable_mp_static ( _regenerate_script_doc_cache ) , CONNECT_ONE_SHOT ) ;
return ;
}
_wait_for_thread ( worker_thread ) ;
_wait_for_thread ( loader_thread ) ;
loader_thread . start ( _regen_script_doc_thread , EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) ) ;
}
// Runs on worker_thread since it writes to DocData.
void EditorHelp : : _finish_regen_script_doc_thread ( void * p_udata ) {
loader_thread . wait_to_finish ( ) ;
_process_postponed_docs ( ) ;
_script_docs_loaded . set ( ) ;
OS : : get_singleton ( ) - > benchmark_end_measure ( " EditorHelp " , " Generate Script Documentation " ) ;
}
// Runs on loader_thread since _reload_scripts_documentation calls ResourceLoader::load().
// Avoids deadlocks of worker_thread needing main thread for load task dispatching, but main thread waiting on worker_thread.
void EditorHelp : : _regen_script_doc_thread ( void * p_udata ) {
OS : : get_singleton ( ) - > benchmark_begin_measure ( " EditorHelp " , " Generate Script Documentation " ) ;
EditorFileSystemDirectory * dir = static_cast < EditorFileSystemDirectory * > ( p_udata ) ;
_script_docs_loaded . set_to ( false ) ;
// Ignore changes from filesystem scan since script docs will be now.
_docs_to_add . clear ( ) ;
_docs_to_remove . clear ( ) ;
_docs_to_remove_by_path . clear ( ) ;
_reload_scripts_documentation ( dir ) ;
// All ResourceLoader::load() calls are done, so we can no longer deadlock with main thread.
// Switch to back to worker_thread from loader_thread to resynchronize access to DocData.
worker_thread . start ( _finish_regen_script_doc_thread , nullptr ) ;
}
void EditorHelp : : _reload_scripts_documentation ( EditorFileSystemDirectory * p_dir ) {
// Recursively force compile all scripts, which should generate their documentation.
for ( int i = 0 ; i < p_dir - > get_subdir_count ( ) ; i + + ) {
_reload_scripts_documentation ( p_dir - > get_subdir ( i ) ) ;
}
for ( int i = 0 ; i < p_dir - > get_file_count ( ) ; i + + ) {
if ( ClassDB : : is_parent_class ( p_dir - > get_file_type ( i ) , SNAME ( " Script " ) ) ) {
Ref < Script > scr = ResourceLoader : : load ( p_dir - > get_file_path ( i ) ) ;
if ( scr . is_valid ( ) ) {
for ( const DocData : : ClassDoc & cd : scr - > get_documentation ( ) ) {
_docs_to_add . push_back ( cd ) ;
}
}
}
}
}
void EditorHelp : : _delete_script_doc_cache ( ) {
if ( FileAccess : : exists ( get_script_doc_cache_full_path ( ) ) ) {
DirAccess : : remove_file_or_error ( ProjectSettings : : get_singleton ( ) - > globalize_path ( get_script_doc_cache_full_path ( ) ) ) ;
}
}
void EditorHelp : : save_script_doc_cache ( ) {
if ( ! _script_docs_loaded . is_set ( ) ) {
print_verbose ( " Script docs haven't been properly loaded or regenerated, so don't save them to disk. " ) ;
return ;
}
Ref < Resource > cache_res ;
cache_res . instantiate ( ) ;
Array classes ;
for ( const KeyValue < String , DocData : : ClassDoc > & E : doc - > class_list ) {
if ( E . value . is_script_doc ) {
classes . push_back ( DocData : : ClassDoc : : to_dict ( E . value ) ) ;
}
}
cache_res - > set_meta ( " classes " , classes ) ;
Error err = ResourceSaver : : save ( cache_res , get_script_doc_cache_full_path ( ) , ResourceSaver : : FLAG_COMPRESS ) ;
ERR_FAIL_COND_MSG ( err ! = OK , vformat ( " Cannot save script documentation cache in %s. " , get_script_doc_cache_full_path ( ) ) ) ;
}
void EditorHelp : : generate_doc ( bool p_use_cache , bool p_use_script_cache ) {
2023-12-08 12:52:49 +01:00
doc_generation_count + + ;
OS : : get_singleton ( ) - > benchmark_begin_measure ( " EditorHelp " , vformat ( " Generate Documentation (Run %d) " , doc_generation_count ) ) ;
2023-02-07 21:14:00 +01:00
2023-10-31 18:27:17 +01:00
// In case not the first attempt.
_wait_for_thread ( ) ;
2023-02-07 21:14:00 +01:00
if ( ! doc ) {
doc = memnew ( DocTools ) ;
}
2023-10-20 22:14:04 +02:00
if ( doc_version_hash . is_empty ( ) ) {
_compute_doc_version_hash ( ) ;
}
2023-10-31 18:27:17 +01:00
if ( p_use_cache & & FileAccess : : exists ( get_cache_full_path ( ) ) ) {
2024-08-19 17:03:03 -04:00
worker_thread . start ( _load_doc_thread , ( void * ) p_use_script_cache ) ;
2021-07-25 14:44:03 -03:00
} else {
2023-02-07 21:14:00 +01:00
print_verbose ( " Regenerating editor help cache " ) ;
2023-10-31 18:27:17 +01:00
doc - > generate ( ) ;
2024-08-19 17:03:03 -04:00
worker_thread . start ( _gen_doc_thread , ( void * ) p_use_script_cache ) ;
2021-07-25 14:44:03 -03:00
}
}
2025-04-07 19:07:33 +02:00
void EditorHelp : : _toggle_files_pressed ( ) {
ScriptEditor : : get_singleton ( ) - > toggle_files_panel ( ) ;
update_toggle_files_button ( ) ;
2021-09-14 12:17:47 +03:00
}
2015-11-17 09:46:08 -03:00
void EditorHelp : : _notification ( int p_what ) {
switch ( p_what ) {
2023-02-11 16:29:44 +01:00
case NOTIFICATION_POSTINITIALIZE : {
// Requires theme to be up to date.
_class_desc_resized ( false ) ;
} break ;
2017-09-02 19:16:31 +02:00
case EditorSettings : : NOTIFICATION_EDITOR_SETTINGS_CHANGED : {
2024-03-08 22:16:55 +03:00
bool need_update = false ;
if ( EditorSettings : : get_singleton ( ) - > check_changed_settings_in_group ( " text_editor/help " ) ) {
need_update = true ;
}
# if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
if ( ! need_update & & EditorSettings : : get_singleton ( ) - > check_changed_settings_in_group ( " text_editor/theme/highlighting " ) ) {
need_update = true ;
}
# endif
if ( ! need_update ) {
2022-11-23 00:14:08 +01:00
break ;
}
[[fallthrough]] ;
}
case NOTIFICATION_READY : {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2019-08-09 01:00:44 -03:00
_update_doc ( ) ;
2017-09-02 19:16:31 +02:00
} break ;
2022-02-16 00:52:32 +01:00
2019-10-15 09:54:28 +03:00
case NOTIFICATION_THEME_CHANGED : {
2022-08-29 11:04:31 +02:00
if ( is_inside_tree ( ) ) {
_class_desc_resized ( true ) ;
}
2025-04-07 19:07:33 +02:00
update_toggle_files_button ( ) ;
2019-10-15 09:54:28 +03:00
} break ;
2022-02-16 00:52:32 +01:00
case NOTIFICATION_VISIBILITY_CHANGED : {
2025-05-30 01:28:43 +02:00
if ( update_pending & & is_visible_in_tree ( ) ) {
_update_doc ( ) ;
}
update_toggle_files_button ( ) ;
} break ;
case NOTIFICATION_TRANSLATION_CHANGED : {
if ( ! is_ready ( ) ) {
break ;
}
if ( is_visible_in_tree ( ) ) {
_update_doc ( ) ;
} else {
update_pending = true ;
}
[[fallthrough]] ;
}
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED : {
2025-04-07 19:07:33 +02:00
update_toggle_files_button ( ) ;
2022-02-16 00:52:32 +01:00
} break ;
2014-02-09 22:10:30 -03:00
}
2015-11-17 09:46:08 -03:00
}
2014-02-09 22:10:30 -03:00
2015-11-17 09:46:08 -03:00
void EditorHelp : : go_to_help ( const String & p_help ) {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2015-11-17 09:46:08 -03:00
_help_callback ( p_help ) ;
2014-02-09 22:10:30 -03:00
}
2023-04-21 09:32:26 -04:00
void EditorHelp : : go_to_class ( const String & p_class ) {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2023-04-21 09:32:26 -04:00
_goto_desc ( p_class ) ;
2015-11-17 09:46:08 -03:00
}
2014-02-15 21:16:33 -03:00
2020-11-29 08:07:57 +05:30
void EditorHelp : : update_doc ( ) {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2020-11-29 08:07:57 +05:30
ERR_FAIL_COND ( ! doc - > class_list . has ( edited_class ) ) ;
ERR_FAIL_COND ( ! doc - > class_list [ edited_class ] . is_script_doc ) ;
_update_doc ( ) ;
}
2021-07-25 14:44:03 -03:00
void EditorHelp : : cleanup_doc ( ) {
_wait_for_thread ( ) ;
memdelete ( doc ) ;
2024-08-09 02:42:58 +02:00
doc = nullptr ;
2021-07-25 14:44:03 -03:00
}
2020-03-17 07:33:00 +01:00
Vector < Pair < String , int > > EditorHelp : : get_sections ( ) {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2020-03-17 07:33:00 +01:00
Vector < Pair < String , int > > sections ;
2017-09-13 19:56:37 -05:00
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 ) {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2017-09-13 19:56:37 -05:00
int line = section_line [ p_section_index ] . second ;
2024-07-04 16:55:51 +02:00
if ( class_desc - > is_finished ( ) ) {
2022-05-30 10:04:07 +03:00
class_desc - > scroll_to_paragraph ( line ) ;
} else {
scroll_to = line ;
}
2017-09-13 19:56:37 -05:00
}
2015-11-17 09:46:08 -03:00
void EditorHelp : : popup_search ( ) {
2021-07-25 14:44:03 -03:00
_wait_for_thread ( ) ;
2017-12-22 16:09:31 -02:00
find_bar - > popup_search ( ) ;
2015-11-17 09:46:08 -03:00
}
2014-02-09 22:10:30 -03:00
2017-01-02 23:03:46 -03:00
String EditorHelp : : get_class ( ) {
2015-11-17 09:46:08 -03:00
return edited_class ;
}
2014-02-09 22:10:30 -03:00
2019-10-09 17:41:49 +02:00
void EditorHelp : : search_again ( bool p_search_previous ) {
_search ( p_search_previous ) ;
2015-11-17 09:46:08 -03:00
}
2014-02-09 22:10:30 -03:00
2015-11-17 09:46:08 -03:00
int EditorHelp : : get_scroll ( ) const {
2021-11-30 13:46:36 -03:00
return class_desc - > get_v_scroll_bar ( ) - > get_value ( ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2015-11-17 09:46:08 -03:00
void EditorHelp : : set_scroll ( int p_scroll ) {
2021-11-30 13:46:36 -03:00
class_desc - > get_v_scroll_bar ( ) - > set_value ( p_scroll ) ;
2014-02-09 22:10:30 -03:00
}
2025-04-07 19:07:33 +02:00
void EditorHelp : : update_toggle_files_button ( ) {
2021-09-14 12:17:47 +03:00
if ( is_layout_rtl ( ) ) {
2025-04-07 19:07:33 +02:00
toggle_files_button - > set_button_icon ( get_editor_theme_icon ( ScriptEditor : : get_singleton ( ) - > is_files_panel_toggled ( ) ? SNAME ( " Forward " ) : SNAME ( " Back " ) ) ) ;
2021-09-14 12:17:47 +03:00
} else {
2025-04-07 19:07:33 +02:00
toggle_files_button - > set_button_icon ( get_editor_theme_icon ( ScriptEditor : : get_singleton ( ) - > is_files_panel_toggled ( ) ? SNAME ( " Back " ) : SNAME ( " Forward " ) ) ) ;
2021-09-14 12:17:47 +03:00
}
2025-04-07 19:07:33 +02:00
toggle_files_button - > set_tooltip_text ( vformat ( " %s (%s) " , TTR ( " Toggle Files Panel " ) , ED_GET_SHORTCUT ( " script_editor/toggle_files_panel " ) - > get_as_text ( ) ) ) ;
2021-09-14 12:17:47 +03:00
}
2014-02-09 22:10:30 -03:00
void EditorHelp : : _bind_methods ( ) {
2017-01-02 23:03:46 -03:00
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 ) ;
2014-02-09 22:10:30 -03:00
2015-11-17 09:46:08 -03:00
ADD_SIGNAL ( MethodInfo ( " go_to_help " ) ) ;
2022-10-22 23:54:37 +02:00
ADD_SIGNAL ( MethodInfo ( " request_save_history " ) ) ;
2014-02-09 22:10:30 -03:00
}
2023-10-19 00:50:30 +02:00
void EditorHelp : : init_gdext_pointers ( ) {
GDExtensionEditorHelp : : editor_help_load_xml_buffer = & EditorHelp : : load_xml_buffer ;
GDExtensionEditorHelp : : editor_help_remove_class = & EditorHelp : : remove_class ;
}
2015-11-17 09:46:08 -03:00
EditorHelp : : EditorHelp ( ) {
2017-12-18 15:46:17 -02:00
set_custom_minimum_size ( Size2 ( 150 * EDSCALE , 0 ) ) ;
2014-02-09 22:10:30 -03:00
2017-12-18 15:46:17 -02:00
class_desc = memnew ( RichTextLabel ) ;
2024-01-17 15:22:43 +01:00
class_desc - > set_tab_size ( 8 ) ;
2017-12-18 15:46:17 -02:00
add_child ( class_desc ) ;
2022-05-18 10:17:55 +03:00
class_desc - > set_threaded ( true ) ;
2017-12-18 15:46:17 -02:00
class_desc - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
2019-06-05 20:47:34 +02:00
2024-05-13 16:56:03 +02:00
class_desc - > connect ( SceneStringName ( finished ) , callable_mp ( this , & EditorHelp : : _class_desc_finished ) ) ;
2020-02-21 18:28:45 +01:00
class_desc - > connect ( " meta_clicked " , callable_mp ( this , & EditorHelp : : _class_desc_select ) ) ;
2024-05-13 16:56:03 +02:00
class_desc - > connect ( SceneStringName ( gui_input ) , callable_mp ( this , & EditorHelp : : _class_desc_input ) ) ;
class_desc - > connect ( SceneStringName ( resized ) , callable_mp ( this , & EditorHelp : : _class_desc_resized ) . bind ( false ) ) ;
2014-02-09 22:10:30 -03:00
2019-02-10 14:17:29 -02:00
// Added second so it opens at the bottom so it won't offset the entire widget.
2019-01-31 15:24:33 +01:00
find_bar = memnew ( FindBar ) ;
add_child ( find_bar ) ;
find_bar - > hide ( ) ;
2017-12-22 16:09:31 -02:00
find_bar - > set_rich_text_label ( class_desc ) ;
2021-09-14 12:17:47 +03:00
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 ) ) ;
2025-04-07 19:07:33 +02:00
toggle_files_button = memnew ( Button ) ;
toggle_files_button - > set_accessibility_name ( TTRC ( " Scripts " ) ) ;
2025-05-30 01:28:43 +02:00
toggle_files_button - > set_tooltip_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2025-04-07 19:07:33 +02:00
toggle_files_button - > set_flat ( true ) ;
toggle_files_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorHelp : : _toggle_files_pressed ) ) ;
status_bar - > add_child ( toggle_files_button ) ;
2021-09-14 12:17:47 +03:00
2014-02-09 22:10:30 -03:00
class_desc - > set_selection_enabled ( true ) ;
2023-02-28 16:19:48 +03:00
class_desc - > set_context_menu_enabled ( true ) ;
2024-12-14 21:25:05 +03:00
class_desc - > set_selection_modifier ( callable_mp_static ( _replace_nbsp_with_space ) ) ;
2015-11-17 09:46:08 -03:00
2014-02-09 22:10:30 -03:00
class_desc - > hide ( ) ;
}
2023-09-20 23:54:51 -03:00
/// EditorHelpBit ///
2022-02-02 14:10:15 +01:00
2024-04-11 11:21:44 +03:00
# define HANDLE_DOC(m_string) ((is_native ? DTR(m_string) : (m_string)).strip_edges())
2016-08-23 23:15:16 -03:00
2024-04-11 11:21:44 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_class_help_data ( const StringName & p_class_name ) {
2023-09-20 23:54:51 -03:00
if ( doc_class_cache . has ( p_class_name ) ) {
return doc_class_cache [ p_class_name ] ;
}
2024-04-11 11:21:44 +03:00
HelpData result ;
2024-03-08 22:16:55 +03:00
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2023-09-20 23:54:51 -03:00
// Non-native class shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2024-04-11 11:21:44 +03:00
2024-08-19 17:03:03 -04:00
const String brief_description = HANDLE_DOC ( class_doc - > brief_description ) ;
const String long_description = HANDLE_DOC ( class_doc - > description ) ;
2024-12-14 21:25:05 +03:00
if ( ! brief_description . is_empty ( ) ) {
result . description + = " [b] " + brief_description + " [/b] " ;
}
if ( ! long_description . is_empty ( ) ) {
if ( ! result . description . is_empty ( ) ) {
result . description + = ' \n ' ;
}
result . description + = long_description ;
}
2024-08-19 17:03:03 -04:00
if ( class_doc - > is_deprecated ) {
if ( class_doc - > deprecated_message . is_empty ( ) ) {
2024-04-11 11:21:44 +03:00
result . deprecated_message = TTR ( " This class may be changed or removed in future versions. " ) ;
} else {
2024-08-19 17:03:03 -04:00
result . deprecated_message = HANDLE_DOC ( class_doc - > deprecated_message ) ;
2024-04-11 11:21:44 +03:00
}
}
2024-08-19 17:03:03 -04:00
if ( class_doc - > is_experimental ) {
if ( class_doc - > experimental_message . is_empty ( ) ) {
2024-04-11 11:21:44 +03:00
result . experimental_message = TTR ( " This class may be changed or removed in future versions. " ) ;
} else {
2024-08-19 17:03:03 -04:00
result . experimental_message = HANDLE_DOC ( class_doc - > experimental_message ) ;
2024-04-11 11:21:44 +03:00
}
}
2023-09-20 23:54:51 -03:00
if ( is_native ) {
2024-04-11 11:21:44 +03:00
doc_class_cache [ p_class_name ] = result ;
2023-09-20 23:54:51 -03:00
}
}
2024-04-11 11:21:44 +03:00
return result ;
2023-09-20 23:54:51 -03:00
}
2024-12-14 21:25:05 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_enum_help_data ( const StringName & p_class_name , const StringName & p_enum_name ) {
if ( doc_enum_cache . has ( p_class_name ) & & doc_enum_cache [ p_class_name ] . has ( p_enum_name ) ) {
return doc_enum_cache [ p_class_name ] [ p_enum_name ] ;
}
HelpData result ;
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2024-12-14 21:25:05 +03:00
// Non-native enums shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2024-12-14 21:25:05 +03:00
2024-08-19 17:03:03 -04:00
for ( const KeyValue < String , DocData : : EnumDoc > & kv : class_doc - > enums ) {
2024-12-14 21:25:05 +03:00
const StringName enum_name = kv . key ;
const DocData : : EnumDoc & enum_doc = kv . value ;
HelpData current ;
current . description = HANDLE_DOC ( enum_doc . description ) ;
if ( enum_doc . is_deprecated ) {
if ( enum_doc . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This enumeration may be changed or removed in future versions. " ) ;
} else {
current . deprecated_message = HANDLE_DOC ( enum_doc . deprecated_message ) ;
}
}
if ( enum_doc . is_experimental ) {
if ( enum_doc . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This enumeration may be changed or removed in future versions. " ) ;
} else {
current . experimental_message = HANDLE_DOC ( enum_doc . experimental_message ) ;
}
}
if ( enum_name = = p_enum_name ) {
result = current ;
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
doc_enum_cache [ p_class_name ] [ enum_name ] = current ;
}
}
}
return result ;
}
EditorHelpBit : : HelpData EditorHelpBit : : _get_constant_help_data ( const StringName & p_class_name , const StringName & p_constant_name ) {
if ( doc_constant_cache . has ( p_class_name ) & & doc_constant_cache [ p_class_name ] . has ( p_constant_name ) ) {
return doc_constant_cache [ p_class_name ] [ p_constant_name ] ;
}
HelpData result ;
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2024-12-14 21:25:05 +03:00
// Non-native constants shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2024-12-14 21:25:05 +03:00
2024-08-19 17:03:03 -04:00
for ( const DocData : : ConstantDoc & constant : class_doc - > constants ) {
2024-12-14 21:25:05 +03:00
HelpData current ;
current . description = HANDLE_DOC ( constant . description ) ;
if ( constant . is_deprecated ) {
if ( constant . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This constant may be changed or removed in future versions. " ) ;
} else {
current . deprecated_message = HANDLE_DOC ( constant . deprecated_message ) ;
}
}
if ( constant . is_experimental ) {
if ( constant . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This constant may be changed or removed in future versions. " ) ;
} else {
current . experimental_message = HANDLE_DOC ( constant . experimental_message ) ;
}
}
current . doc_type = { constant . type , constant . enumeration , constant . is_bitfield } ;
if ( constant . is_value_valid ) {
current . value = constant . value ;
}
if ( constant . name = = p_constant_name ) {
result = current ;
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
doc_constant_cache [ p_class_name ] [ constant . name ] = current ;
}
}
}
return result ;
}
2024-04-11 11:21:44 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_property_help_data ( const StringName & p_class_name , const StringName & p_property_name ) {
2023-09-20 23:54:51 -03:00
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 ] ;
}
2024-04-11 11:21:44 +03:00
HelpData result ;
2024-03-08 22:16:55 +03:00
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2024-03-08 22:16:55 +03:00
// Non-native properties shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2024-03-08 22:16:55 +03:00
2024-08-19 17:03:03 -04:00
for ( const DocData : : PropertyDoc & property : class_doc - > properties ) {
2024-04-11 11:21:44 +03:00
HelpData current ;
current . description = HANDLE_DOC ( property . description ) ;
if ( property . is_deprecated ) {
if ( property . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This property may be changed or removed in future versions. " ) ;
} else {
current . deprecated_message = HANDLE_DOC ( property . deprecated_message ) ;
}
}
if ( property . is_experimental ) {
if ( property . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This property may be changed or removed in future versions. " ) ;
} else {
current . experimental_message = HANDLE_DOC ( property . experimental_message ) ;
}
}
2024-12-14 21:25:05 +03:00
current . doc_type = { property . type , property . enumeration , property . is_bitfield } ;
current . value = property . default_value ;
2023-09-20 23:54:51 -03:00
2024-04-10 15:35:48 +03:00
String enum_class_name ;
String enum_name ;
if ( CoreConstants : : is_global_enum ( property . enumeration ) ) {
enum_class_name = " @GlobalScope " ;
enum_name = property . enumeration ;
} else {
2024-11-16 18:52:15 +01:00
const int dot_pos = property . enumeration . rfind_char ( ' . ' ) ;
2024-04-10 15:35:48 +03:00
if ( dot_pos > = 0 ) {
enum_class_name = property . enumeration . left ( dot_pos ) ;
enum_name = property . enumeration . substr ( dot_pos + 1 ) ;
}
}
if ( ! enum_class_name . is_empty ( ) & & ! enum_name . is_empty ( ) ) {
2023-09-20 23:54:51 -03:00
// Classes can use enums from other classes, so check from which it came.
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * enum_class = EditorHelp : : get_doc ( enum_class_name ) ;
2023-09-20 23:54:51 -03:00
if ( enum_class ) {
2024-04-10 15:35:48 +03:00
const String enum_prefix = EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( enum_name , EditorPropertyNameProcessor : : STYLE_CAPITALIZED ) + " " ;
2024-08-19 17:03:03 -04:00
for ( DocData : : ConstantDoc constant : enum_class - > constants ) {
2023-09-20 23:54:51 -03:00
// Don't display `_MAX` enum value descriptions, as these are never exposed in the inspector.
2024-04-10 15:35:48 +03:00
if ( constant . enumeration = = enum_name & & ! constant . name . ends_with ( " _MAX " ) ) {
// Prettify the enum value display, so that "<ENUM_NAME>_<ITEM>" becomes "Item".
const String item_name = EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( constant . name , EditorPropertyNameProcessor : : STYLE_CAPITALIZED ) . trim_prefix ( enum_prefix ) ;
2024-04-11 11:21:44 +03:00
String item_descr = HANDLE_DOC ( constant . description ) ;
2024-04-10 15:35:48 +03:00
if ( item_descr . is_empty ( ) ) {
2024-04-11 11:21:44 +03:00
item_descr = " [color=<EditorHelpBitCommentColor>][i] " + TTR ( " No description available. " ) + " [/i][/color] " ;
2024-04-10 15:35:48 +03:00
}
2024-04-11 11:21:44 +03:00
current . description + = vformat ( " \n [b]%s:[/b] %s " , item_name , item_descr ) ;
2023-09-20 23:54:51 -03:00
}
}
2024-04-11 11:21:44 +03:00
current . description = current . description . lstrip ( " \n " ) ;
2023-09-20 23:54:51 -03:00
}
}
2024-02-16 16:28:05 +03:00
if ( property . name = = p_property_name ) {
2024-04-11 11:21:44 +03:00
result = current ;
2023-09-20 23:54:51 -03:00
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
2024-04-11 11:21:44 +03:00
doc_property_cache [ p_class_name ] [ property . name ] = current ;
2023-09-20 23:54:51 -03:00
}
}
}
2024-04-11 11:21:44 +03:00
return result ;
2023-09-20 23:54:51 -03:00
}
2024-12-14 21:25:05 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_theme_item_help_data ( const StringName & p_class_name , const StringName & p_theme_item_name ) {
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 ] ;
}
HelpData result ;
bool found = false ;
2024-08-19 17:03:03 -04:00
DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
while ( class_doc ) {
2024-12-14 21:25:05 +03:00
// Non-native theme items shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2024-12-14 21:25:05 +03:00
2024-08-19 17:03:03 -04:00
for ( const DocData : : ThemeItemDoc & theme_item : class_doc - > theme_properties ) {
2024-12-14 21:25:05 +03:00
HelpData current ;
current . description = HANDLE_DOC ( theme_item . description ) ;
if ( theme_item . is_deprecated ) {
if ( theme_item . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This theme property may be changed or removed in future versions. " ) ;
} else {
current . deprecated_message = HANDLE_DOC ( theme_item . deprecated_message ) ;
}
}
if ( theme_item . is_experimental ) {
if ( theme_item . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This theme property may be changed or removed in future versions. " ) ;
} else {
current . experimental_message = HANDLE_DOC ( theme_item . experimental_message ) ;
}
}
current . doc_type = { theme_item . type , String ( ) , false } ;
current . value = theme_item . default_value ;
if ( theme_item . name = = p_theme_item_name ) {
result = current ;
found = true ;
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
doc_theme_item_cache [ p_class_name ] [ theme_item . name ] = current ;
}
}
2024-08-19 17:03:03 -04:00
if ( found | | class_doc - > inherits . is_empty ( ) ) {
2024-12-14 21:25:05 +03:00
break ;
}
// Check for inherited theme items.
2024-08-19 17:03:03 -04:00
class_doc = EditorHelp : : get_doc ( class_doc - > inherits ) ;
2024-12-14 21:25:05 +03:00
}
return result ;
}
2024-04-11 11:21:44 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_method_help_data ( const StringName & p_class_name , const StringName & p_method_name ) {
2023-09-20 23:54:51 -03:00
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 ] ;
}
2024-04-11 11:21:44 +03:00
HelpData result ;
2024-03-08 22:16:55 +03:00
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2023-09-20 23:54:51 -03:00
// Non-native methods shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2023-09-20 23:54:51 -03:00
2024-08-19 17:03:03 -04:00
for ( const DocData : : MethodDoc & method : class_doc - > methods ) {
2024-04-11 11:21:44 +03:00
HelpData current ;
current . description = HANDLE_DOC ( method . description ) ;
if ( method . is_deprecated ) {
if ( method . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This method may be changed or removed in future versions. " ) ;
} else {
current . deprecated_message = HANDLE_DOC ( method . deprecated_message ) ;
}
}
if ( method . is_experimental ) {
if ( method . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This method may be changed or removed in future versions. " ) ;
} else {
current . experimental_message = HANDLE_DOC ( method . experimental_message ) ;
}
}
current . doc_type = { method . return_type , method . return_enum , method . return_is_bitfield } ;
for ( const DocData : : ArgumentDoc & argument : method . arguments ) {
2025-03-30 12:59:05 +03:00
const DocType argument_doc_type = { argument . type , argument . enumeration , argument . is_bitfield } ;
current . arguments . push_back ( { argument . name , argument_doc_type , argument . default_value } ) ;
2024-04-11 11:21:44 +03:00
}
2024-12-14 21:25:05 +03:00
current . qualifiers = method . qualifiers ;
2025-03-30 12:59:05 +03:00
const DocData : : ArgumentDoc & rest_argument = method . rest_argument ;
const DocType rest_argument_doc_type = { rest_argument . type , rest_argument . enumeration , rest_argument . is_bitfield } ;
current . rest_argument = { rest_argument . name , rest_argument_doc_type , rest_argument . default_value } ;
2023-09-20 23:54:51 -03:00
2024-02-16 16:28:05 +03:00
if ( method . name = = p_method_name ) {
2024-04-11 11:21:44 +03:00
result = current ;
2023-09-20 23:54:51 -03:00
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
2024-04-11 11:21:44 +03:00
doc_method_cache [ p_class_name ] [ method . name ] = current ;
2023-09-20 23:54:51 -03:00
}
}
}
2024-04-11 11:21:44 +03:00
return result ;
2023-09-20 23:54:51 -03:00
}
2024-04-11 11:21:44 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_signal_help_data ( const StringName & p_class_name , const StringName & p_signal_name ) {
2023-09-20 23:54:51 -03:00
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 ] ;
}
2024-04-11 11:21:44 +03:00
HelpData result ;
2024-03-08 22:16:55 +03:00
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2023-09-20 23:54:51 -03:00
// Non-native signals shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2023-09-20 23:54:51 -03:00
2024-08-19 17:03:03 -04:00
for ( const DocData : : MethodDoc & signal : class_doc - > signals ) {
2024-04-11 11:21:44 +03:00
HelpData current ;
current . description = HANDLE_DOC ( signal . description ) ;
if ( signal . is_deprecated ) {
if ( signal . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This signal may be changed or removed in future versions. " ) ;
} else {
current . deprecated_message = HANDLE_DOC ( signal . deprecated_message ) ;
}
}
if ( signal . is_experimental ) {
if ( signal . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This signal may be changed or removed in future versions. " ) ;
} else {
current . experimental_message = HANDLE_DOC ( signal . experimental_message ) ;
}
}
for ( const DocData : : ArgumentDoc & argument : signal . arguments ) {
const DocType argument_type = { argument . type , argument . enumeration , argument . is_bitfield } ;
current . arguments . push_back ( { argument . name , argument_type , argument . default_value } ) ;
}
2024-12-14 21:25:05 +03:00
current . qualifiers = signal . qualifiers ;
2023-09-20 23:54:51 -03:00
2024-02-16 16:28:05 +03:00
if ( signal . name = = p_signal_name ) {
2024-04-11 11:21:44 +03:00
result = current ;
2023-09-20 23:54:51 -03:00
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
2024-04-11 11:21:44 +03:00
doc_signal_cache [ p_class_name ] [ signal . name ] = current ;
2023-09-20 23:54:51 -03:00
}
}
}
2024-04-11 11:21:44 +03:00
return result ;
2023-09-20 23:54:51 -03:00
}
2024-12-14 21:25:05 +03:00
EditorHelpBit : : HelpData EditorHelpBit : : _get_annotation_help_data ( const StringName & p_class_name , const StringName & p_annotation_name ) {
if ( doc_annotation_cache . has ( p_class_name ) & & doc_annotation_cache [ p_class_name ] . has ( p_annotation_name ) ) {
return doc_annotation_cache [ p_class_name ] [ p_annotation_name ] ;
2023-09-20 23:54:51 -03:00
}
2024-04-11 11:21:44 +03:00
HelpData result ;
2024-03-08 22:16:55 +03:00
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( p_class_name ) ;
if ( class_doc ) {
2024-12-14 21:25:05 +03:00
// Non-native annotations shouldn't be cached, nor translated.
2024-08-19 17:03:03 -04:00
const bool is_native = ! class_doc - > is_script_doc ;
2023-09-20 23:54:51 -03:00
2024-08-19 17:03:03 -04:00
for ( const DocData : : MethodDoc & annotation : class_doc - > annotations ) {
2024-04-11 11:21:44 +03:00
HelpData current ;
2024-12-14 21:25:05 +03:00
current . description = HANDLE_DOC ( annotation . description ) ;
if ( annotation . is_deprecated ) {
if ( annotation . deprecated_message . is_empty ( ) ) {
current . deprecated_message = TTR ( " This annotation may be changed or removed in future versions. " ) ;
2024-09-04 10:54:50 +03:00
} else {
2024-12-14 21:25:05 +03:00
current . deprecated_message = HANDLE_DOC ( annotation . deprecated_message ) ;
2024-09-04 10:54:50 +03:00
}
}
2024-12-14 21:25:05 +03:00
if ( annotation . is_experimental ) {
if ( annotation . experimental_message . is_empty ( ) ) {
current . experimental_message = TTR ( " This annotation may be changed or removed in future versions. " ) ;
2024-09-04 10:54:50 +03:00
} else {
2024-12-14 21:25:05 +03:00
current . experimental_message = HANDLE_DOC ( annotation . experimental_message ) ;
2024-09-04 10:54:50 +03:00
}
}
2024-12-14 21:25:05 +03:00
for ( const DocData : : ArgumentDoc & argument : annotation . arguments ) {
const DocType argument_type = { argument . type , argument . enumeration , argument . is_bitfield } ;
current . arguments . push_back ( { argument . name , argument_type , argument . default_value } ) ;
}
current . qualifiers = annotation . qualifiers ;
2023-09-20 23:54:51 -03:00
2024-12-14 21:25:05 +03:00
if ( annotation . name = = p_annotation_name ) {
2024-04-11 11:21:44 +03:00
result = current ;
2024-05-28 02:33:27 -03:00
2023-09-20 23:54:51 -03:00
if ( ! is_native ) {
break ;
}
}
if ( is_native ) {
2024-12-14 21:25:05 +03:00
doc_annotation_cache [ p_class_name ] [ annotation . name ] = current ;
2023-09-20 23:54:51 -03:00
}
}
}
2024-04-11 11:21:44 +03:00
return result ;
}
# undef HANDLE_DOC
void EditorHelpBit : : _add_type_to_title ( const DocType & p_doc_type ) {
_add_type_to_rt ( p_doc_type . type , p_doc_type . enumeration , p_doc_type . is_bitfield , title , this , symbol_class_name ) ;
}
void EditorHelpBit : : _update_labels ( ) {
const Ref < Font > doc_bold_font = get_theme_font ( SNAME ( " doc_bold " ) , EditorStringName ( EditorFonts ) ) ;
2024-12-14 21:25:05 +03:00
if ( ! symbol_type . is_empty ( ) | | ! symbol_name . is_empty ( ) ) {
2024-04-11 11:21:44 +03:00
title - > clear ( ) ;
title - > push_font ( doc_bold_font ) ;
2024-12-14 21:25:05 +03:00
if ( ! symbol_type . is_empty ( ) ) {
2024-04-11 11:21:44 +03:00
title - > push_color ( get_theme_color ( SNAME ( " title_color " ) , SNAME ( " EditorHelp " ) ) ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( symbol_type ) ;
2024-04-11 11:21:44 +03:00
title - > pop ( ) ; // color
}
2024-12-14 21:25:05 +03:00
if ( ! symbol_type . is_empty ( ) & & ! symbol_name . is_empty ( ) ) {
2024-04-11 11:21:44 +03:00
title - > add_text ( " " ) ;
}
if ( ! symbol_name . is_empty ( ) ) {
2024-12-14 21:25:05 +03:00
if ( ! symbol_doc_link . is_empty ( ) ) {
title - > push_meta ( symbol_doc_link , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
}
if ( use_class_prefix & & ! symbol_class_name . is_empty ( ) & & symbol_hint ! = SYMBOL_HINT_INHERITANCE ) {
title - > add_text ( symbol_class_name + " . " ) ;
}
2024-04-11 11:21:44 +03:00
title - > add_text ( symbol_name ) ;
2024-12-14 21:25:05 +03:00
if ( ! symbol_doc_link . is_empty ( ) ) {
title - > pop ( ) ; // meta
}
2024-04-11 11:21:44 +03:00
}
title - > pop ( ) ; // font
2025-03-30 12:59:05 +03:00
const Color text_color = get_theme_color ( SNAME ( " text_color " ) , SNAME ( " EditorHelp " ) ) ;
2024-12-14 21:25:05 +03:00
const Color symbol_color = get_theme_color ( SNAME ( " symbol_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color value_color = get_theme_color ( SNAME ( " value_color " ) , SNAME ( " EditorHelp " ) ) ;
const Color qualifier_color = get_theme_color ( SNAME ( " qualifier_color " ) , SNAME ( " EditorHelp " ) ) ;
const Ref < Font > doc_source = get_theme_font ( SNAME ( " doc_source " ) , EditorStringName ( EditorFonts ) ) ;
const int doc_source_size = get_theme_font_size ( SNAME ( " doc_source_size " ) , EditorStringName ( EditorFonts ) ) ;
2024-04-11 11:21:44 +03:00
2024-12-14 21:25:05 +03:00
switch ( symbol_hint ) {
case SYMBOL_HINT_NONE : {
// Nothing to do.
} break ;
case SYMBOL_HINT_INHERITANCE : {
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * class_doc = EditorHelp : : get_doc ( symbol_class_name ) ;
2024-12-14 21:25:05 +03:00
String inherits = class_doc ? class_doc - > inherits : String ( ) ;
2024-04-11 11:21:44 +03:00
2024-12-14 21:25:05 +03:00
if ( ! inherits . is_empty ( ) ) {
title - > push_font ( doc_source ) ;
title - > push_font_size ( doc_source_size * 0.9 ) ;
2024-04-11 11:21:44 +03:00
2024-12-14 21:25:05 +03:00
while ( ! inherits . is_empty ( ) ) {
title - > push_color ( symbol_color ) ;
title - > add_text ( " < " + nbsp ) ;
title - > pop ( ) ; // color
2024-04-11 11:21:44 +03:00
2024-12-14 21:25:05 +03:00
_add_type_to_title ( { inherits , String ( ) , false } ) ;
2024-08-19 17:03:03 -04:00
const DocData : : ClassDoc * base_class_doc = EditorHelp : : get_doc ( inherits ) ;
2025-02-11 22:01:22 +01:00
inherits = base_class_doc ? base_class_doc - > inherits : String ( ) ;
2024-12-14 21:25:05 +03:00
}
title - > pop ( ) ; // font_size
title - > pop ( ) ; // font
2024-04-11 11:21:44 +03:00
}
2024-12-14 21:25:05 +03:00
} break ;
case SYMBOL_HINT_ASSIGNABLE : {
const bool has_type = ! help_data . doc_type . type . is_empty ( ) ;
const bool has_value = ! help_data . value . is_empty ( ) ;
if ( has_type | | has_value ) {
title - > push_font ( doc_source ) ;
title - > push_font_size ( doc_source_size * 0.9 ) ;
if ( has_type ) {
title - > push_color ( symbol_color ) ;
title - > add_text ( colon_nbsp ) ;
title - > pop ( ) ; // color
2024-04-11 11:21:44 +03:00
2024-12-14 21:25:05 +03:00
_add_type_to_title ( help_data . doc_type ) ;
}
if ( has_value ) {
title - > push_color ( symbol_color ) ;
title - > add_text ( nbsp_equal_nbsp ) ;
title - > pop ( ) ; // color
title - > push_color ( value_color ) ;
title - > add_text ( _fix_constant ( help_data . value ) ) ;
title - > pop ( ) ; // color
}
title - > pop ( ) ; // font_size
title - > pop ( ) ; // font
}
} break ;
case SYMBOL_HINT_SIGNATURE : {
title - > push_font ( doc_source ) ;
title - > push_font_size ( doc_source_size * 0.9 ) ;
2024-04-11 11:21:44 +03:00
title - > push_color ( symbol_color ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( " ( " ) ;
2024-04-11 11:21:44 +03:00
title - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
for ( int i = 0 ; i < help_data . arguments . size ( ) ; i + + ) {
const ArgumentData & argument = help_data . arguments [ i ] ;
if ( i > 0 ) {
2025-03-30 12:59:05 +03:00
title - > push_color ( symbol_color ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( " , " ) ;
2025-03-30 12:59:05 +03:00
title - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
}
2025-03-30 12:59:05 +03:00
title - > push_color ( text_color ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( argument . name ) ;
2025-03-30 12:59:05 +03:00
title - > pop ( ) ; // color
2024-04-11 11:21:44 +03:00
title - > push_color ( symbol_color ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( colon_nbsp ) ;
2024-04-11 11:21:44 +03:00
title - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
_add_type_to_title ( argument . doc_type ) ;
if ( ! argument . default_value . is_empty ( ) ) {
title - > push_color ( symbol_color ) ;
title - > add_text ( nbsp_equal_nbsp ) ;
title - > pop ( ) ; // color
title - > push_color ( value_color ) ;
title - > add_text ( _fix_constant ( argument . default_value ) ) ;
title - > pop ( ) ; // color
}
2024-04-11 11:21:44 +03:00
}
2024-12-14 21:25:05 +03:00
if ( help_data . qualifiers . contains ( " vararg " ) ) {
if ( ! help_data . arguments . is_empty ( ) ) {
2025-03-30 12:59:05 +03:00
title - > push_color ( symbol_color ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( " , " ) ;
2025-03-30 12:59:05 +03:00
title - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
}
title - > push_color ( symbol_color ) ;
title - > add_text ( " ... " ) ;
title - > pop ( ) ; // color
2025-03-30 12:59:05 +03:00
const ArgumentData & rest_argument = help_data . rest_argument ;
title - > push_color ( text_color ) ;
title - > add_text ( rest_argument . name . is_empty ( ) ? " args " : rest_argument . name ) ;
title - > pop ( ) ; // color
title - > push_color ( symbol_color ) ;
title - > add_text ( colon_nbsp ) ;
title - > pop ( ) ; // color
if ( rest_argument . doc_type . type . is_empty ( ) ) {
_add_type_to_title ( { " Array " , " " , false } ) ;
} else {
_add_type_to_title ( rest_argument . doc_type ) ;
}
2024-12-14 21:25:05 +03:00
}
2024-04-11 11:21:44 +03:00
title - > push_color ( symbol_color ) ;
2024-12-14 21:25:05 +03:00
title - > add_text ( " ) " ) ;
2024-04-11 11:21:44 +03:00
title - > pop ( ) ; // color
2024-12-14 21:25:05 +03:00
if ( ! help_data . doc_type . type . is_empty ( ) ) {
title - > push_color ( symbol_color ) ;
title - > add_text ( " -> " + nbsp ) ;
title - > pop ( ) ; // color
_add_type_to_title ( help_data . doc_type ) ;
}
2024-04-11 11:21:44 +03:00
2024-12-14 21:25:05 +03:00
if ( ! help_data . qualifiers . is_empty ( ) ) {
title - > push_color ( qualifier_color ) ;
_add_qualifiers_to_rt ( help_data . qualifiers , title ) ;
title - > pop ( ) ; // color
}
title - > pop ( ) ; // font_size
title - > pop ( ) ; // font
} break ;
2024-04-11 11:21:44 +03:00
}
title - > show ( ) ;
} else {
title - > hide ( ) ;
}
content - > clear ( ) ;
bool has_prev_text = false ;
if ( ! help_data . deprecated_message . is_empty ( ) ) {
has_prev_text = true ;
Ref < Texture2D > error_icon = get_editor_theme_icon ( SNAME ( " StatusError " ) ) ;
content - > add_image ( error_icon , error_icon - > get_width ( ) , error_icon - > get_height ( ) ) ;
2024-12-14 21:25:05 +03:00
content - > add_text ( nbsp ) ;
2024-04-11 11:21:44 +03:00
content - > push_color ( get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
content - > push_font ( doc_bold_font ) ;
content - > add_text ( TTR ( " Deprecated: " ) ) ;
content - > pop ( ) ; // font
content - > pop ( ) ; // color
content - > add_text ( " " ) ;
_add_text_to_rt ( help_data . deprecated_message , content , this , symbol_class_name ) ;
}
if ( ! help_data . experimental_message . is_empty ( ) ) {
if ( has_prev_text ) {
content - > add_newline ( ) ;
content - > add_newline ( ) ;
}
has_prev_text = true ;
Ref < Texture2D > warning_icon = get_editor_theme_icon ( SNAME ( " NodeWarning " ) ) ;
content - > add_image ( warning_icon , warning_icon - > get_width ( ) , warning_icon - > get_height ( ) ) ;
2024-12-14 21:25:05 +03:00
content - > add_text ( nbsp ) ;
2024-04-11 11:21:44 +03:00
content - > push_color ( get_theme_color ( SNAME ( " warning_color " ) , EditorStringName ( Editor ) ) ) ;
content - > push_font ( doc_bold_font ) ;
content - > add_text ( TTR ( " Experimental: " ) ) ;
content - > pop ( ) ; // font
content - > pop ( ) ; // color
content - > add_text ( " " ) ;
_add_text_to_rt ( help_data . experimental_message , content , this , symbol_class_name ) ;
}
if ( ! help_data . description . is_empty ( ) ) {
if ( has_prev_text ) {
content - > add_newline ( ) ;
content - > add_newline ( ) ;
}
has_prev_text = true ;
const Color comment_color = get_theme_color ( SNAME ( " comment_color " ) , SNAME ( " EditorHelp " ) ) ;
_add_text_to_rt ( help_data . description . replace ( " <EditorHelpBitCommentColor> " , comment_color . to_html ( ) ) , content , this , symbol_class_name ) ;
}
2024-12-25 03:52:07 +01:00
if ( ! help_data . resource_path . is_empty ( ) ) {
if ( has_prev_text ) {
content - > add_newline ( ) ;
content - > add_newline ( ) ;
}
has_prev_text = true ;
const String ext = help_data . resource_path . get_extension ( ) ;
const bool is_dir = ext . is_empty ( ) ;
const bool is_valid = is_dir | | EditorFileSystem : : get_singleton ( ) - > get_valid_extensions ( ) . has ( ext ) ;
if ( ! is_dir & & is_valid ) {
content - > push_meta ( " open-res: " + help_data . resource_path , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
content - > add_image ( get_editor_theme_icon ( SNAME ( " Load " ) ) ) ;
content - > add_text ( nbsp + TTR ( " Open " ) ) ;
content - > pop ( ) ; // meta
content - > add_newline ( ) ;
}
if ( is_valid ) {
content - > push_meta ( " show: " + help_data . resource_path , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
content - > add_image ( get_editor_theme_icon ( SNAME ( " Filesystem " ) ) ) ;
content - > add_text ( nbsp + TTR ( " Show in FileSystem " ) ) ;
content - > pop ( ) ; // meta
} else {
content - > push_meta ( " open-file: " + help_data . resource_path , RichTextLabel : : META_UNDERLINE_ON_HOVER ) ;
content - > add_image ( get_editor_theme_icon ( SNAME ( " Filesystem " ) ) ) ;
content - > add_text ( nbsp + TTR ( " Open in File Manager " ) ) ;
content - > pop ( ) ; // meta
}
}
2024-04-11 11:21:44 +03:00
if ( is_inside_tree ( ) ) {
update_content_height ( ) ;
}
}
void EditorHelpBit : : _go_to_help ( const String & p_what ) {
2024-08-19 18:08:31 -04:00
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > select ( EditorMainScreen : : EDITOR_SCRIPT ) ;
2024-04-11 11:21:44 +03:00
ScriptEditor : : get_singleton ( ) - > goto_help ( p_what ) ;
emit_signal ( SNAME ( " request_hide " ) ) ;
}
void EditorHelpBit : : _meta_clicked ( const String & p_select ) {
if ( p_select . begins_with ( " $ " ) ) { // Enum.
const String link = p_select . substr ( 1 ) ;
String enum_class_name ;
String enum_name ;
if ( CoreConstants : : is_global_enum ( link ) ) {
enum_class_name = " @GlobalScope " ;
enum_name = link ;
} else {
2024-11-16 18:52:15 +01:00
const int dot_pos = link . rfind_char ( ' . ' ) ;
2024-04-11 11:21:44 +03:00
if ( dot_pos > = 0 ) {
enum_class_name = link . left ( dot_pos ) ;
enum_name = link . substr ( dot_pos + 1 ) ;
} else {
enum_class_name = symbol_class_name ;
enum_name = link ;
}
}
_go_to_help ( " class_enum: " + enum_class_name + " : " + enum_name ) ;
} else if ( p_select . begins_with ( " # " ) ) { // Class.
_go_to_help ( " class_name: " + p_select . substr ( 1 ) ) ;
} else if ( p_select . begins_with ( " @ " ) ) { // Member.
const int tag_end = p_select . find_char ( ' ' ) ;
const String tag = p_select . substr ( 1 , tag_end - 1 ) ;
const String link = p_select . substr ( tag_end + 1 ) . lstrip ( " " ) ;
String topic ;
if ( tag = = " method " ) {
topic = " class_method " ;
} else if ( tag = = " constructor " ) {
topic = " class_method " ;
} else if ( tag = = " operator " ) {
topic = " class_method " ;
} else if ( tag = = " member " ) {
topic = " class_property " ;
} else if ( tag = = " enum " ) {
topic = " class_enum " ;
} else if ( tag = = " signal " ) {
topic = " class_signal " ;
} else if ( tag = = " constant " ) {
topic = " class_constant " ;
} else if ( tag = = " annotation " ) {
topic = " class_annotation " ;
} else if ( tag = = " theme_item " ) {
topic = " class_theme_item " ;
} else {
return ;
}
2024-12-14 21:25:05 +03:00
if ( topic = = " class_enum " ) {
const String enum_link = link . trim_prefix ( " @GlobalScope. " ) ;
if ( CoreConstants : : is_global_enum ( enum_link ) ) {
_go_to_help ( topic + " :@GlobalScope: " + enum_link ) ;
return ;
}
} else if ( topic = = " class_constant " ) {
if ( CoreConstants : : is_global_constant ( link ) ) {
_go_to_help ( topic + " :@GlobalScope: " + link ) ;
return ;
}
}
2024-12-05 17:56:08 +01:00
if ( link . contains_char ( ' . ' ) ) {
2024-12-14 21:25:05 +03:00
const int class_end = link . rfind_char ( ' . ' ) ;
2024-04-11 11:21:44 +03:00
_go_to_help ( topic + " : " + link . left ( class_end ) + " : " + link . substr ( class_end + 1 ) ) ;
} else {
_go_to_help ( topic + " : " + symbol_class_name + " : " + link ) ;
}
2024-12-25 03:52:07 +01:00
} else if ( p_select . begins_with ( " open-file: " ) ) {
String path = ProjectSettings : : get_singleton ( ) - > globalize_path ( p_select . trim_prefix ( " open-file: " ) ) ;
OS : : get_singleton ( ) - > shell_show_in_file_manager ( path , true ) ;
} else if ( p_select . begins_with ( " open-res: " ) ) {
2024-12-28 17:40:50 +01:00
EditorNode : : get_singleton ( ) - > load_scene_or_resource ( p_select . trim_prefix ( " open-res: " ) ) ;
2024-12-25 03:52:07 +01:00
} else if ( p_select . begins_with ( " show: " ) ) {
FileSystemDock : : get_singleton ( ) - > navigate_to_path ( p_select . trim_prefix ( " show: " ) ) ;
2024-04-11 11:21:44 +03:00
} else if ( p_select . begins_with ( " http: " ) | | p_select . begins_with ( " https: " ) ) {
OS : : get_singleton ( ) - > shell_open ( p_select ) ;
} else if ( p_select . begins_with ( " ^ " ) ) { // Copy button.
DisplayServer : : get_singleton ( ) - > clipboard_set ( p_select . substr ( 1 ) ) ;
}
2023-09-20 23:54:51 -03:00
}
2016-08-23 23:15:16 -03:00
void EditorHelpBit : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " request_hide " ) ) ;
}
void EditorHelpBit : : _notification ( int p_what ) {
2017-09-02 19:16:31 +02:00
switch ( p_what ) {
2024-04-11 11:21:44 +03:00
case NOTIFICATION_THEME_CHANGED :
_update_labels ( ) ;
break ;
2017-09-02 19:16:31 +02:00
}
2016-08-23 23:15:16 -03:00
}
2024-08-01 20:03:15 -04:00
void EditorHelpBit : : parse_symbol ( const String & p_symbol , const String & p_prologue ) {
2024-12-14 21:25:05 +03:00
const PackedStringArray slices = p_symbol . split ( " | " , true , 3 ) ;
ERR_FAIL_COND_MSG ( slices . size ( ) < 3 , R " (Invalid doc id: The expected format is " item_type | class_name | item_name [ | item_data ] " .) " ) ;
2024-04-11 11:21:44 +03:00
const String & item_type = slices [ 0 ] ;
const String & class_name = slices [ 1 ] ;
const String & item_name = slices [ 2 ] ;
2024-12-14 21:25:05 +03:00
Dictionary item_data ;
if ( slices . size ( ) > 3 ) {
item_data = JSON : : parse_string ( slices [ 3 ] ) ;
}
symbol_doc_link = String ( ) ;
symbol_class_name = class_name ;
symbol_type = String ( ) ;
symbol_name = item_name ;
symbol_hint = SYMBOL_HINT_NONE ;
help_data = HelpData ( ) ;
2024-04-11 11:21:44 +03:00
if ( item_type = = " class " ) {
2024-12-14 21:25:05 +03:00
symbol_doc_link = vformat ( " #%s " , class_name ) ;
symbol_type = TTR ( " Class " ) ;
symbol_name = class_name ;
symbol_hint = SYMBOL_HINT_INHERITANCE ;
2024-04-11 11:21:44 +03:00
help_data = _get_class_help_data ( class_name ) ;
2024-12-14 21:25:05 +03:00
} else if ( item_type = = " enum " ) {
symbol_doc_link = vformat ( " $%s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Enumeration " ) ;
help_data = _get_enum_help_data ( class_name , item_name ) ;
} else if ( item_type = = " constant " ) {
symbol_doc_link = vformat ( " @constant %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Constant " ) ;
symbol_hint = SYMBOL_HINT_ASSIGNABLE ;
help_data = _get_constant_help_data ( class_name , item_name ) ;
2024-04-11 11:21:44 +03:00
} else if ( item_type = = " property " ) {
2024-12-14 21:25:05 +03:00
if ( item_name . begins_with ( " metadata/ " ) ) {
symbol_type = TTR ( " Metadata " ) ;
symbol_name = item_name . trim_prefix ( " metadata/ " ) ;
2024-04-11 11:21:44 +03:00
} else if ( class_name = = " ProjectSettings " | | class_name = = " EditorSettings " ) {
2024-12-14 21:25:05 +03:00
symbol_doc_link = vformat ( " @member %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Setting " ) ;
symbol_hint = SYMBOL_HINT_ASSIGNABLE ;
2024-04-11 11:21:44 +03:00
} else {
2024-12-14 21:25:05 +03:00
symbol_doc_link = vformat ( " @member %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Property " ) ;
symbol_hint = SYMBOL_HINT_ASSIGNABLE ;
2024-04-11 11:21:44 +03:00
}
help_data = _get_property_help_data ( class_name , item_name ) ;
} else if ( item_type = = " internal_property " ) {
2024-12-14 21:25:05 +03:00
symbol_type = TTR ( " Internal Property " ) ;
2024-04-11 11:21:44 +03:00
help_data . description = " [color=<EditorHelpBitCommentColor>][i] " + TTR ( " This property can only be set in the Inspector. " ) + " [/i][/color] " ;
2024-12-14 21:25:05 +03:00
} else if ( item_type = = " theme_item " ) {
symbol_doc_link = vformat ( " @theme_item %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Theme Property " ) ;
symbol_hint = SYMBOL_HINT_ASSIGNABLE ;
help_data = _get_theme_item_help_data ( class_name , item_name ) ;
2024-04-11 11:21:44 +03:00
} else if ( item_type = = " method " ) {
2024-12-14 21:25:05 +03:00
symbol_doc_link = vformat ( " @method %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Method " ) ;
symbol_hint = SYMBOL_HINT_SIGNATURE ;
2024-04-11 11:21:44 +03:00
help_data = _get_method_help_data ( class_name , item_name ) ;
} else if ( item_type = = " signal " ) {
2024-12-14 21:25:05 +03:00
symbol_doc_link = vformat ( " @signal %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Signal " ) ;
symbol_hint = SYMBOL_HINT_SIGNATURE ;
2024-04-11 11:21:44 +03:00
help_data = _get_signal_help_data ( class_name , item_name ) ;
2024-12-14 21:25:05 +03:00
} else if ( item_type = = " annotation " ) {
symbol_doc_link = vformat ( " @annotation %s.%s " , class_name , item_name ) ;
symbol_type = TTR ( " Annotation " ) ;
symbol_hint = SYMBOL_HINT_SIGNATURE ;
help_data = _get_annotation_help_data ( class_name , item_name ) ;
} else if ( item_type = = " local_constant " | | item_type = = " local_variable " ) {
symbol_type = ( item_type = = " local_constant " ) ? TTR ( " Local Constant " ) : TTR ( " Local Variable " ) ;
symbol_hint = SYMBOL_HINT_ASSIGNABLE ;
help_data . description = item_data . get ( " description " , " " ) . operator String ( ) . strip_edges ( ) ;
if ( item_data . get ( " is_deprecated " , false ) ) {
const String deprecated_message = item_data . get ( " deprecated_message " , " " ) . operator String ( ) . strip_edges ( ) ;
if ( deprecated_message . is_empty ( ) ) {
if ( item_type = = " local_constant " ) {
help_data . deprecated_message = TTR ( " This constant may be changed or removed in future versions. " ) ;
} else {
help_data . deprecated_message = TTR ( " This variable may be changed or removed in future versions. " ) ;
}
} else {
help_data . deprecated_message = deprecated_message ;
}
}
if ( item_data . get ( " is_experimental " , false ) ) {
const String experimental_message = item_data . get ( " experimental_message " , " " ) . operator String ( ) . strip_edges ( ) ;
if ( experimental_message . is_empty ( ) ) {
if ( item_type = = " local_constant " ) {
help_data . experimental_message = TTR ( " This constant may be changed or removed in future versions. " ) ;
} else {
help_data . experimental_message = TTR ( " This variable may be changed or removed in future versions. " ) ;
}
} else {
help_data . experimental_message = experimental_message ;
}
}
help_data . doc_type . type = item_data . get ( " doc_type " , " " ) ;
help_data . doc_type . enumeration = item_data . get ( " enumeration " , " " ) ;
help_data . doc_type . is_bitfield = item_data . get ( " is_bitfield " , false ) ;
help_data . value = item_data . get ( " value " , " " ) ;
2024-12-25 03:52:07 +01:00
} else if ( item_type = = " resource " ) {
String path = item_name . simplify_path ( ) ;
const bool is_uid = path . begins_with ( " uid:// " ) ;
if ( is_uid ) {
if ( ResourceUID : : get_singleton ( ) - > has_id ( ResourceUID : : get_singleton ( ) - > text_to_id ( path ) ) ) {
path = ResourceUID : : uid_to_path ( path ) ;
} else {
path = " " ;
}
}
help_data . resource_path = path ;
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
if ( da - > file_exists ( path ) ) {
help_data . doc_type . type = ResourceLoader : : get_resource_type ( path ) ;
if ( help_data . doc_type . type . is_empty ( ) ) {
const Vector < String > textfile_ext = ( ( String ) ( EDITOR_GET ( " docks/filesystem/textfile_extensions " ) ) ) . split ( " , " , false ) ;
2025-03-06 20:46:04 +08:00
symbol_type = textfile_ext . has ( path . get_extension ( ) ) ? TTR ( " Text File " ) : TTR ( " File " ) ;
2024-12-25 03:52:07 +01:00
} else {
symbol_type = TTR ( " Resource " ) ;
symbol_hint = SYMBOL_HINT_ASSIGNABLE ;
if ( is_uid ) {
help_data . description = vformat ( " %s: [color=<EditorHelpBitCommentColor>]%s[/color] " , TTR ( " Path " ) , path ) ;
}
}
symbol_name = path . get_file ( ) ;
} else if ( ! is_uid & & da - > dir_exists ( path ) ) {
symbol_type = TTR ( " Directory " ) ;
symbol_name = path ;
} else {
help_data . resource_path = " " ;
symbol_name = " " ;
if ( is_uid ) {
symbol_type = TTR ( " Invalid UID " ) ;
help_data . description = " [color=<EditorHelpBitCommentColor>][i] " + TTR ( " This UID does not point to any valid Resource. " ) + " [/i][/color] " ;
} else {
symbol_type = TTR ( " Invalid path " ) ;
help_data . description = " [color=<EditorHelpBitCommentColor>][i] " + TTR ( " This path does not exist. " ) + " [/i][/color] " ;
}
}
2024-04-11 11:21:44 +03:00
} else {
2024-12-14 21:25:05 +03:00
ERR_FAIL_MSG ( " Invalid doc id: Unknown item type " + item_type . quote ( ) + " . " ) ;
2024-04-11 11:21:44 +03:00
}
2024-12-14 21:25:05 +03:00
// Do not add links for custom or undocumented symbols.
if ( symbol_class_name . is_empty ( ) | | ( help_data . description . is_empty ( ) & & help_data . deprecated_message . is_empty ( ) & & help_data . experimental_message . is_empty ( ) ) ) {
symbol_doc_link = String ( ) ;
}
2024-04-11 11:21:44 +03:00
2024-08-01 20:03:15 -04:00
if ( ! p_prologue . is_empty ( ) ) {
if ( help_data . description . is_empty ( ) ) {
help_data . description = p_prologue ;
} else {
help_data . description = p_prologue + " \n " + help_data . description ;
}
}
2024-12-25 03:52:07 +01:00
if ( help_data . description . is_empty ( ) & & item_type ! = " resource " ) {
2024-04-11 11:21:44 +03:00
help_data . description = " [color=<EditorHelpBitCommentColor>][i] " + TTR ( " No description available. " ) + " [/i][/color] " ;
}
if ( is_inside_tree ( ) ) {
_update_labels ( ) ;
}
2016-08-23 23:15:16 -03:00
}
2024-04-11 11:21:44 +03:00
void EditorHelpBit : : set_custom_text ( const String & p_type , const String & p_name , const String & p_description ) {
2024-12-14 21:25:05 +03:00
symbol_doc_link = String ( ) ;
2024-04-11 11:21:44 +03:00
symbol_class_name = String ( ) ;
2024-12-14 21:25:05 +03:00
symbol_type = p_type ;
2024-04-11 11:21:44 +03:00
symbol_name = p_name ;
2024-12-14 21:25:05 +03:00
symbol_hint = SYMBOL_HINT_NONE ;
2024-04-11 11:21:44 +03:00
help_data = HelpData ( ) ;
help_data . description = p_description ;
if ( is_inside_tree ( ) ) {
_update_labels ( ) ;
}
2016-08-23 23:15:16 -03:00
}
2017-12-22 16:09:31 -02:00
2024-04-11 11:21:44 +03:00
void EditorHelpBit : : set_content_height_limits ( float p_min , float p_max ) {
ERR_FAIL_COND ( p_min > p_max ) ;
content_min_height = p_min ;
content_max_height = p_max ;
2023-09-20 23:54:51 -03:00
2024-04-11 11:21:44 +03:00
if ( is_inside_tree ( ) ) {
update_content_height ( ) ;
}
}
2023-09-20 23:54:51 -03:00
2024-04-11 11:21:44 +03:00
void EditorHelpBit : : update_content_height ( ) {
float content_height = content - > get_content_height ( ) ;
2024-05-14 14:41:39 +02:00
const Ref < StyleBox > style = content - > get_theme_stylebox ( CoreStringName ( normal ) ) ;
2024-04-11 11:21:44 +03:00
if ( style . is_valid ( ) ) {
content_height + = style - > get_content_margin ( SIDE_TOP ) + style - > get_content_margin ( SIDE_BOTTOM ) ;
}
content - > set_custom_minimum_size ( Size2 ( content - > get_custom_minimum_size ( ) . x , CLAMP ( content_height , content_min_height , content_max_height ) ) ) ;
}
2023-09-20 23:54:51 -03:00
2024-12-14 21:25:05 +03:00
EditorHelpBit : : EditorHelpBit ( const String & p_symbol , const String & p_prologue , bool p_use_class_prefix , bool p_allow_selection ) {
2024-04-11 11:21:44 +03:00
add_theme_constant_override ( " separation " , 0 ) ;
2024-03-08 22:16:55 +03:00
2024-04-11 11:21:44 +03:00
title = memnew ( RichTextLabel ) ;
title - > set_theme_type_variation ( " EditorHelpBitTitle " ) ;
2024-12-14 21:25:05 +03:00
title - > set_custom_minimum_size ( Size2 ( 640 * EDSCALE , 0 ) ) ; // GH-93031. Set the minimum width even if `fit_content` is true.
2024-04-11 11:21:44 +03:00
title - > set_fit_content ( true ) ;
2024-08-01 20:03:15 -04:00
title - > set_selection_enabled ( p_allow_selection ) ;
title - > set_context_menu_enabled ( p_allow_selection ) ;
2024-12-14 21:25:05 +03:00
title - > set_selection_modifier ( callable_mp_static ( _replace_nbsp_with_space ) ) ;
2024-04-11 11:21:44 +03:00
title - > connect ( " meta_clicked " , callable_mp ( this , & EditorHelpBit : : _meta_clicked ) ) ;
title - > hide ( ) ;
add_child ( title ) ;
2024-01-26 20:42:20 +01:00
2024-04-11 11:21:44 +03:00
content_min_height = 48 * EDSCALE ;
content_max_height = 360 * EDSCALE ;
content = memnew ( RichTextLabel ) ;
content - > set_theme_type_variation ( " EditorHelpBitContent " ) ;
2024-12-14 21:25:05 +03:00
content - > set_custom_minimum_size ( Size2 ( 640 * EDSCALE , content_min_height ) ) ;
2024-08-01 20:03:15 -04:00
content - > set_selection_enabled ( p_allow_selection ) ;
content - > set_context_menu_enabled ( p_allow_selection ) ;
2024-12-14 21:25:05 +03:00
content - > set_selection_modifier ( callable_mp_static ( _replace_nbsp_with_space ) ) ;
2024-04-11 11:21:44 +03:00
content - > connect ( " meta_clicked " , callable_mp ( this , & EditorHelpBit : : _meta_clicked ) ) ;
add_child ( content ) ;
2024-12-14 21:25:05 +03:00
use_class_prefix = p_use_class_prefix ;
2024-04-11 11:21:44 +03:00
if ( ! p_symbol . is_empty ( ) ) {
2024-08-01 20:03:15 -04:00
parse_symbol ( p_symbol , p_prologue ) ;
2024-12-14 21:25:05 +03:00
} else if ( ! p_prologue . is_empty ( ) ) {
set_custom_text ( String ( ) , String ( ) , p_prologue ) ;
2024-01-26 20:42:20 +01:00
}
2024-04-11 11:21:44 +03:00
}
2024-01-26 20:42:20 +01:00
2024-04-11 11:21:44 +03:00
/// EditorHelpBitTooltip ///
2024-08-01 20:03:15 -04:00
bool EditorHelpBitTooltip : : _is_tooltip_visible = false ;
Control * EditorHelpBitTooltip : : _make_invisible_control ( ) {
Control * control = memnew ( Control ) ;
control - > set_visible ( false ) ;
return control ;
}
2024-07-05 12:23:13 +03:00
void EditorHelpBitTooltip : : _start_timer ( ) {
if ( timer - > is_inside_tree ( ) & & timer - > is_stopped ( ) ) {
timer - > start ( ) ;
}
}
void EditorHelpBitTooltip : : _target_gui_input ( const Ref < InputEvent > & p_event ) {
2024-08-01 20:03:15 -04:00
// Only scrolling is not checked in `NOTIFICATION_INTERNAL_PROCESS`.
const Ref < InputEventMouseButton > mb = p_event ;
if ( mb . is_valid ( ) ) {
switch ( mb - > get_button_index ( ) ) {
case MouseButton : : WHEEL_UP :
case MouseButton : : WHEEL_DOWN :
case MouseButton : : WHEEL_LEFT :
case MouseButton : : WHEEL_RIGHT :
queue_free ( ) ;
break ;
default :
break ;
}
2024-07-05 12:23:13 +03:00
}
}
2024-04-11 11:21:44 +03:00
void EditorHelpBitTooltip : : _notification ( int p_what ) {
switch ( p_what ) {
2024-08-01 20:03:15 -04:00
case NOTIFICATION_ENTER_TREE :
_is_tooltip_visible = true ;
_enter_tree_time = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
break ;
case NOTIFICATION_EXIT_TREE :
_is_tooltip_visible = false ;
break ;
2024-04-11 11:21:44 +03:00
case NOTIFICATION_WM_MOUSE_ENTER :
2024-08-01 20:03:15 -04:00
_is_mouse_inside_tooltip = true ;
2024-04-11 11:21:44 +03:00
timer - > stop ( ) ;
break ;
case NOTIFICATION_WM_MOUSE_EXIT :
2024-08-01 20:03:15 -04:00
_is_mouse_inside_tooltip = false ;
2024-07-05 12:23:13 +03:00
_start_timer ( ) ;
2024-04-11 11:21:44 +03:00
break ;
2024-08-01 20:03:15 -04:00
case NOTIFICATION_INTERNAL_PROCESS :
// A workaround to hide the tooltip since the window does not receive keyboard events
// with `FLAG_POPUP` and `FLAG_NO_FOCUS` flags, so we can't use `_input_from_window()`.
if ( is_inside_tree ( ) ) {
if ( Input : : get_singleton ( ) - > is_action_just_pressed ( SNAME ( " ui_cancel " ) , true ) ) {
queue_free ( ) ;
get_parent_viewport ( ) - > set_input_as_handled ( ) ;
} else if ( Input : : get_singleton ( ) - > is_anything_pressed_except_mouse ( ) ) {
queue_free ( ) ;
} else if ( ! Input : : get_singleton ( ) - > get_mouse_button_mask ( ) . is_empty ( ) ) {
if ( ! _is_mouse_inside_tooltip ) {
queue_free ( ) ;
}
} else if ( ! Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) . is_zero_approx ( ) ) {
2025-04-20 13:46:07 -03:00
if ( ! _is_mouse_inside_tooltip & & OS : : get_singleton ( ) - > get_ticks_msec ( ) - _enter_tree_time > 350 ) {
2024-08-01 20:03:15 -04:00
_start_timer ( ) ;
}
}
}
break ;
2024-04-11 11:21:44 +03:00
}
}
2023-09-20 23:54:51 -03:00
2024-12-14 21:25:05 +03:00
Control * EditorHelpBitTooltip : : show_tooltip ( Control * p_target , const String & p_symbol , const String & p_prologue , bool p_use_class_prefix ) {
ERR_FAIL_NULL_V ( p_target , _make_invisible_control ( ) ) ;
2024-08-01 20:03:15 -04:00
// Show the custom tooltip only if it is not already visible.
// The viewport will retrigger `make_custom_tooltip()` every few seconds
// because the return control is not visible even if the custom tooltip is displayed.
if ( _is_tooltip_visible | | Input : : get_singleton ( ) - > is_anything_pressed ( ) ) {
return _make_invisible_control ( ) ;
2024-04-11 11:21:44 +03:00
}
2023-09-20 23:54:51 -03:00
2024-12-14 21:25:05 +03:00
EditorHelpBit * help_bit = memnew ( EditorHelpBit ( p_symbol , p_prologue , p_use_class_prefix , false ) ) ;
2024-08-01 20:03:15 -04:00
2024-04-11 11:21:44 +03:00
EditorHelpBitTooltip * tooltip = memnew ( EditorHelpBitTooltip ( p_target ) ) ;
2024-08-01 20:03:15 -04:00
help_bit - > connect ( " request_hide " , callable_mp ( static_cast < Node * > ( tooltip ) , & Node : : queue_free ) ) ;
tooltip - > add_child ( help_bit ) ;
2024-10-14 09:48:58 +08:00
p_target - > add_child ( tooltip ) ;
2024-08-01 20:03:15 -04:00
help_bit - > update_content_height ( ) ;
2024-04-11 11:21:44 +03:00
tooltip - > popup_under_cursor ( ) ;
2024-08-01 20:03:15 -04:00
return _make_invisible_control ( ) ;
2024-04-11 11:21:44 +03:00
}
// Copy-paste from `Viewport::_gui_show_tooltip()`.
void EditorHelpBitTooltip : : popup_under_cursor ( ) {
Point2 mouse_pos = get_mouse_position ( ) ;
2025-04-29 14:48:52 +01:00
Point2 tooltip_offset = GLOBAL_GET_CACHED ( Point2 , " display/mouse_cursor/tooltip_position_offset " ) ;
2024-04-11 11:21:44 +03:00
Rect2 r ( mouse_pos + tooltip_offset , get_contents_minimum_size ( ) ) ;
r . size = r . size . min ( get_max_size ( ) ) ;
Window * window = get_parent_visible_window ( ) ;
Rect2i vr ;
if ( is_embedded ( ) ) {
vr = get_embedder ( ) - > get_visible_rect ( ) ;
} else {
vr = window - > get_usable_parent_rect ( ) ;
}
if ( r . size . x + r . position . x > vr . size . x + vr . position . x ) {
// Place it in the opposite direction. If it fails, just hug the border.
r . position . x = mouse_pos . x - r . size . x - tooltip_offset . x ;
if ( r . position . x < vr . position . x ) {
r . position . x = vr . position . x + vr . size . x - r . size . x ;
}
} else if ( r . position . x < vr . position . x ) {
r . position . x = vr . position . x ;
}
if ( r . size . y + r . position . y > vr . size . y + vr . position . y ) {
// Same as above.
r . position . y = mouse_pos . y - r . size . y - tooltip_offset . y ;
if ( r . position . y < vr . position . y ) {
r . position . y = vr . position . y + vr . size . y - r . size . y ;
2023-09-20 23:54:51 -03:00
}
2024-04-11 11:21:44 +03:00
} else if ( r . position . y < vr . position . y ) {
r . position . y = vr . position . y ;
2023-09-20 23:54:51 -03:00
}
2024-08-01 20:03:15 -04:00
// When `FLAG_POPUP` is false, it prevents the editor from losing focus when displaying the tooltip.
// This way, clicks and double-clicks are still available outside the tooltip.
set_flag ( Window : : FLAG_POPUP , false ) ;
2025-04-07 10:38:10 +03:00
set_flag ( Window : : FLAG_NO_FOCUS , ! is_embedded ( ) ) ;
2024-04-11 11:21:44 +03:00
popup ( r ) ;
2023-09-20 23:54:51 -03:00
}
2024-04-11 11:21:44 +03:00
EditorHelpBitTooltip : : EditorHelpBitTooltip ( Control * p_target ) {
2024-12-14 21:25:05 +03:00
ERR_FAIL_NULL ( p_target ) ;
2024-04-11 11:21:44 +03:00
set_theme_type_variation ( " TooltipPanel " ) ;
timer = memnew ( Timer ) ;
2024-08-01 20:03:15 -04:00
timer - > set_wait_time ( 0.25 ) ;
timer - > connect ( " timeout " , callable_mp ( static_cast < Node * > ( this ) , & Node : : queue_free ) ) ;
2024-04-11 11:21:44 +03:00
add_child ( timer ) ;
2023-09-20 23:54:51 -03:00
2024-07-05 12:23:13 +03:00
p_target - > connect ( SceneStringName ( mouse_exited ) , callable_mp ( this , & EditorHelpBitTooltip : : _start_timer ) ) ;
p_target - > connect ( SceneStringName ( gui_input ) , callable_mp ( this , & EditorHelpBitTooltip : : _target_gui_input ) ) ;
2024-08-01 20:03:15 -04:00
set_process_internal ( true ) ;
2023-09-20 23:54:51 -03:00
}
2024-03-08 22:16:55 +03:00
/// EditorHelpHighlighter ///
EditorHelpHighlighter * EditorHelpHighlighter : : singleton = nullptr ;
void EditorHelpHighlighter : : create_singleton ( ) {
ERR_FAIL_COND ( singleton ! = nullptr ) ;
singleton = memnew ( EditorHelpHighlighter ) ;
}
void EditorHelpHighlighter : : free_singleton ( ) {
ERR_FAIL_NULL ( singleton ) ;
memdelete ( singleton ) ;
singleton = nullptr ;
}
EditorHelpHighlighter * EditorHelpHighlighter : : get_singleton ( ) {
return singleton ;
}
EditorHelpHighlighter : : HighlightData EditorHelpHighlighter : : _get_highlight_data ( Language p_language , const String & p_source , bool p_use_cache ) {
switch ( p_language ) {
case LANGUAGE_GDSCRIPT :
# ifndef MODULE_GDSCRIPT_ENABLED
ERR_FAIL_V_MSG ( HighlightData ( ) , " GDScript module is disabled. " ) ;
# endif
break ;
case LANGUAGE_CSHARP :
# ifndef MODULE_MONO_ENABLED
ERR_FAIL_V_MSG ( HighlightData ( ) , " Mono module is disabled. " ) ;
# endif
break ;
default :
ERR_FAIL_V_MSG ( HighlightData ( ) , " Invalid parameter \" p_language \" . " ) ;
}
if ( p_use_cache ) {
const HashMap < String , HighlightData > : : ConstIterator E = highlight_data_caches [ p_language ] . find ( p_source ) ;
if ( E ) {
return E - > value ;
}
}
text_edits [ p_language ] - > set_text ( p_source ) ;
2024-03-24 20:09:07 +03:00
if ( scripts [ p_language ] . is_valid ( ) ) { // See GH-89610.
scripts [ p_language ] - > set_source_code ( p_source ) ;
}
2024-03-08 22:16:55 +03:00
highlighters [ p_language ] - > _update_cache ( ) ;
HighlightData result ;
int source_offset = 0 ;
int result_index = 0 ;
for ( int i = 0 ; i < text_edits [ p_language ] - > get_line_count ( ) ; i + + ) {
const Dictionary dict = highlighters [ p_language ] - > _get_line_syntax_highlighting_impl ( i ) ;
result . resize ( result . size ( ) + dict . size ( ) ) ;
const Variant * key = nullptr ;
int prev_column = - 1 ;
while ( ( key = dict . next ( key ) ) ! = nullptr ) {
const int column = * key ;
ERR_FAIL_COND_V ( column < = prev_column , HighlightData ( ) ) ;
prev_column = column ;
const Color color = dict [ * key ] . operator Dictionary ( ) . get ( " color " , Color ( ) ) ;
result . write [ result_index ] = { source_offset + column , color } ;
result_index + + ;
}
source_offset + = text_edits [ p_language ] - > get_line ( i ) . length ( ) + 1 ; // Plus newline.
}
if ( p_use_cache ) {
highlight_data_caches [ p_language ] [ p_source ] = result ;
}
return result ;
}
void EditorHelpHighlighter : : highlight ( RichTextLabel * p_rich_text_label , Language p_language , const String & p_source , bool p_use_cache ) {
ERR_FAIL_NULL ( p_rich_text_label ) ;
const HighlightData highlight_data = _get_highlight_data ( p_language , p_source , p_use_cache ) ;
if ( ! highlight_data . is_empty ( ) ) {
for ( int i = 1 ; i < highlight_data . size ( ) ; i + + ) {
const Pair < int , Color > & prev = highlight_data [ i - 1 ] ;
const Pair < int , Color > & curr = highlight_data [ i ] ;
p_rich_text_label - > push_color ( prev . second ) ;
p_rich_text_label - > add_text ( p_source . substr ( prev . first , curr . first - prev . first ) ) ;
p_rich_text_label - > pop ( ) ; // color
}
const Pair < int , Color > & last = highlight_data [ highlight_data . size ( ) - 1 ] ;
p_rich_text_label - > push_color ( last . second ) ;
p_rich_text_label - > add_text ( p_source . substr ( last . first ) ) ;
p_rich_text_label - > pop ( ) ; // color
}
}
void EditorHelpHighlighter : : reset_cache ( ) {
const Color text_color = EDITOR_GET ( " text_editor/theme/highlighting/text_color " ) ;
# ifdef MODULE_GDSCRIPT_ENABLED
highlight_data_caches [ LANGUAGE_GDSCRIPT ] . clear ( ) ;
2024-05-14 15:57:29 +02:00
text_edits [ LANGUAGE_GDSCRIPT ] - > add_theme_color_override ( SceneStringName ( font_color ) , text_color ) ;
2024-03-08 22:16:55 +03:00
# endif
# ifdef MODULE_MONO_ENABLED
highlight_data_caches [ LANGUAGE_CSHARP ] . clear ( ) ;
2024-05-14 15:57:29 +02:00
text_edits [ LANGUAGE_CSHARP ] - > add_theme_color_override ( SceneStringName ( font_color ) , text_color ) ;
2024-03-08 22:16:55 +03:00
# endif
}
EditorHelpHighlighter : : EditorHelpHighlighter ( ) {
2024-03-20 09:40:45 +03:00
const Color text_color = EDITOR_GET ( " text_editor/theme/highlighting/text_color " ) ;
2024-03-08 22:16:55 +03:00
# ifdef MODULE_GDSCRIPT_ENABLED
TextEdit * gdscript_text_edit = memnew ( TextEdit ) ;
2024-05-14 15:57:29 +02:00
gdscript_text_edit - > add_theme_color_override ( SceneStringName ( font_color ) , text_color ) ;
2024-03-08 22:16:55 +03:00
Ref < GDScript > gdscript ;
gdscript . instantiate ( ) ;
Ref < GDScriptSyntaxHighlighter > gdscript_highlighter ;
gdscript_highlighter . instantiate ( ) ;
gdscript_highlighter - > set_text_edit ( gdscript_text_edit ) ;
gdscript_highlighter - > _set_edited_resource ( gdscript ) ;
text_edits [ LANGUAGE_GDSCRIPT ] = gdscript_text_edit ;
scripts [ LANGUAGE_GDSCRIPT ] = gdscript ;
highlighters [ LANGUAGE_GDSCRIPT ] = gdscript_highlighter ;
# endif
# ifdef MODULE_MONO_ENABLED
TextEdit * csharp_text_edit = memnew ( TextEdit ) ;
2024-05-14 15:57:29 +02:00
csharp_text_edit - > add_theme_color_override ( SceneStringName ( font_color ) , text_color ) ;
2024-03-08 22:16:55 +03:00
2024-03-24 20:09:07 +03:00
// See GH-89610.
//Ref<CSharpScript> csharp;
//csharp.instantiate();
2024-03-08 22:16:55 +03:00
Ref < EditorStandardSyntaxHighlighter > csharp_highlighter ;
csharp_highlighter . instantiate ( ) ;
csharp_highlighter - > set_text_edit ( csharp_text_edit ) ;
2024-03-24 20:09:07 +03:00
//csharp_highlighter->_set_edited_resource(csharp);
csharp_highlighter - > _set_script_language ( CSharpLanguage : : get_singleton ( ) ) ;
2024-03-08 22:16:55 +03:00
text_edits [ LANGUAGE_CSHARP ] = csharp_text_edit ;
2024-03-24 20:09:07 +03:00
//scripts[LANGUAGE_CSHARP] = csharp;
2024-03-08 22:16:55 +03:00
highlighters [ LANGUAGE_CSHARP ] = csharp_highlighter ;
# endif
}
EditorHelpHighlighter : : ~ EditorHelpHighlighter ( ) {
# ifdef MODULE_GDSCRIPT_ENABLED
memdelete ( text_edits [ LANGUAGE_GDSCRIPT ] ) ;
# endif
# ifdef MODULE_MONO_ENABLED
memdelete ( text_edits [ LANGUAGE_CSHARP ] ) ;
# endif
}
2023-09-20 23:54:51 -03:00
/// FindBar ///
2022-02-02 14:10:15 +01:00
2017-12-22 16:09:31 -02:00
FindBar : : FindBar ( ) {
search_text = memnew ( LineEdit ) ;
2025-02-02 18:31:12 +01:00
search_text - > set_keep_editing_on_text_submit ( true ) ;
2025-04-30 21:43:05 -04:00
add_child ( search_text ) ;
search_text - > set_placeholder ( TTR ( " Search " ) ) ;
search_text - > set_tooltip_text ( TTR ( " Search " ) ) ;
search_text - > set_accessibility_name ( TTRC ( " Search Documentation " ) ) ;
2017-12-22 16:09:31 -02:00
search_text - > set_custom_minimum_size ( Size2 ( 100 * EDSCALE , 0 ) ) ;
2019-02-10 14:17:29 -02:00
search_text - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2024-05-14 11:42:00 +02:00
search_text - > connect ( SceneStringName ( text_changed ) , callable_mp ( this , & FindBar : : _search_text_changed ) ) ;
2024-11-02 16:15:39 +01:00
search_text - > connect ( SceneStringName ( text_submitted ) , callable_mp ( this , & FindBar : : _search_text_submitted ) ) ;
2017-12-22 16:09:31 -02:00
2019-08-09 01:00:44 -03:00
matches_label = memnew ( Label ) ;
add_child ( matches_label ) ;
2025-04-30 21:43:05 -04:00
matches_label - > set_focus_mode ( FOCUS_ACCESSIBILITY ) ;
2019-08-09 01:00:44 -03:00
matches_label - > hide ( ) ;
2020-06-19 20:49:04 +02:00
find_prev = memnew ( Button ) ;
find_prev - > set_flat ( true ) ;
2025-04-30 21:43:05 -04:00
find_prev - > set_disabled ( results_count < 1 ) ;
find_prev - > set_tooltip_text ( TTR ( " Previous Match " ) ) ;
find_prev - > set_accessibility_name ( TTRC ( " Previous Match " ) ) ;
2019-02-10 14:17:29 -02:00
add_child ( find_prev ) ;
2025-04-23 12:46:50 +03:00
find_prev - > set_focus_mode ( FOCUS_ACCESSIBILITY ) ;
2024-05-14 09:40:21 +02:00
find_prev - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FindBar : : search_prev ) ) ;
2017-12-22 16:09:31 -02:00
2020-06-19 20:49:04 +02:00
find_next = memnew ( Button ) ;
find_next - > set_flat ( true ) ;
2025-04-30 21:43:05 -04:00
find_next - > set_disabled ( results_count < 1 ) ;
find_next - > set_tooltip_text ( TTR ( " Next Match " ) ) ;
find_next - > set_accessibility_name ( TTRC ( " Next Match " ) ) ;
2019-02-10 14:17:29 -02:00
add_child ( find_next ) ;
2025-04-23 12:46:50 +03:00
find_next - > set_focus_mode ( FOCUS_ACCESSIBILITY ) ;
2024-05-14 09:40:21 +02:00
find_next - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FindBar : : search_next ) ) ;
2017-12-22 16:09:31 -02:00
2025-04-30 21:43:05 -04:00
hide_button = memnew ( Button ) ;
hide_button - > set_flat ( true ) ;
hide_button - > set_tooltip_text ( TTR ( " Hide " ) ) ;
2025-03-21 09:55:22 +02:00
hide_button - > set_accessibility_name ( TTRC ( " Hide " ) ) ;
2025-04-23 12:46:50 +03:00
hide_button - > set_focus_mode ( FOCUS_ACCESSIBILITY ) ;
2024-05-14 09:40:21 +02:00
hide_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FindBar : : _hide_bar ) ) ;
2025-04-30 21:43:05 -04:00
hide_button - > set_v_size_flags ( SIZE_SHRINK_CENTER ) ;
add_child ( hide_button ) ;
2017-12-22 16:09:31 -02:00
}
void FindBar : : popup_search ( ) {
show ( ) ;
2018-05-16 11:43:00 -03:00
bool grabbed_focus = false ;
if ( ! search_text - > has_focus ( ) ) {
search_text - > grab_focus ( ) ;
grabbed_focus = true ;
}
2020-12-15 12:04:21 +00:00
if ( ! search_text - > get_text ( ) . is_empty ( ) ) {
2018-05-16 11:43:00 -03:00
search_text - > select_all ( ) ;
2021-03-28 19:31:25 +01:00
search_text - > set_caret_column ( search_text - > get_text ( ) . length ( ) ) ;
2018-05-16 11:43:00 -03:00
if ( grabbed_focus ) {
2025-05-15 05:53:29 +00:00
rich_text_label - > deselect ( ) ;
results_count_to_current = 0 ;
2018-05-16 11:43:00 -03:00
_search ( ) ;
}
}
2017-12-22 16:09:31 -02:00
}
void FindBar : : _notification ( int p_what ) {
2019-08-09 01:00:44 -03:00
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
2024-10-09 15:21:47 -07:00
find_prev - > set_button_icon ( get_editor_theme_icon ( SNAME ( " MoveUp " ) ) ) ;
find_next - > set_button_icon ( get_editor_theme_icon ( SNAME ( " MoveDown " ) ) ) ;
2025-04-30 21:43:05 -04:00
hide_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " Close " ) ) ) ;
2024-05-14 15:57:29 +02:00
matches_label - > add_theme_color_override ( SceneStringName ( font_color ) , results_count > 0 ? get_theme_color ( SceneStringName ( font_color ) , SNAME ( " Label " ) ) : get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
2019-08-09 01:00:44 -03:00
} break ;
2022-02-16 00:52:32 +01:00
2019-08-09 01:00:44 -03:00
case NOTIFICATION_VISIBILITY_CHANGED : {
2025-02-02 18:31:12 +01:00
set_process_input ( is_visible_in_tree ( ) ) ;
2019-08-09 01:00:44 -03:00
} break ;
2017-12-22 16:09:31 -02:00
}
}
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 ;
2025-04-30 21:43:05 -04:00
if ( ! keep ) {
results_count_to_current = 0 ;
}
2017-12-22 16:09:31 -02:00
if ( ret ) {
2025-04-30 21:43:05 -04:00
_update_results_count ( p_search_previous ) ;
2017-12-22 16:09:31 -02:00
} else {
2019-08-09 01:00:44 -03:00
results_count = 0 ;
2025-04-30 21:43:05 -04:00
results_count_to_current = 0 ;
2017-12-22 16:09:31 -02:00
}
2019-08-09 01:00:44 -03:00
_update_matches_label ( ) ;
2017-12-22 16:09:31 -02:00
return ret ;
}
2025-04-30 21:43:05 -04:00
void FindBar : : _update_results_count ( bool p_search_previous ) {
2019-08-09 01:00:44 -03:00
results_count = 0 ;
String searched = search_text - > get_text ( ) ;
2020-12-15 12:04:21 +00:00
if ( searched . is_empty ( ) ) {
2019-08-09 01:00:44 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2019-08-09 01:00:44 -03:00
2021-10-01 12:36:37 -03:00
String full_text = rich_text_label - > get_parsed_text ( ) ;
2019-08-09 01:00:44 -03:00
int from_pos = 0 ;
while ( true ) {
2021-06-16 09:43:34 -07:00
int pos = full_text . findn ( searched , from_pos ) ;
2020-05-14 16:41:43 +02:00
if ( pos = = - 1 ) {
2019-08-09 01:00:44 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2019-08-09 01:00:44 -03:00
results_count + + ;
from_pos = pos + searched . length ( ) ;
}
2025-04-30 21:43:05 -04:00
results_count_to_current + = ( p_search_previous ) ? - 1 : 1 ;
if ( results_count_to_current > results_count ) {
results_count_to_current = results_count_to_current - results_count ;
} else if ( results_count_to_current < = 0 ) {
results_count_to_current = results_count ;
}
2019-08-09 01:00:44 -03:00
}
void FindBar : : _update_matches_label ( ) {
2020-12-15 12:04:21 +00:00
if ( search_text - > get_text ( ) . is_empty ( ) | | results_count = = - 1 ) {
2019-08-09 01:00:44 -03:00
matches_label - > hide ( ) ;
} else {
matches_label - > show ( ) ;
2024-05-14 15:57:29 +02:00
matches_label - > add_theme_color_override ( SceneStringName ( font_color ) , results_count > 0 ? get_theme_color ( SceneStringName ( font_color ) , SNAME ( " Label " ) ) : get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
2025-04-30 21:43:05 -04:00
if ( results_count = = 0 ) {
matches_label - > set_text ( TTR ( " No match " ) ) ;
} else if ( results_count_to_current = = 0 ) {
matches_label - > set_text ( vformat ( TTRN ( " %d match " , " %d matches " , results_count ) , results_count ) ) ;
} else {
matches_label - > set_text ( vformat ( TTRN ( " %d of %d match " , " %d of %d matches " , results_count ) , results_count_to_current , results_count ) ) ;
}
2019-08-09 01:00:44 -03:00
}
2025-04-30 21:43:05 -04:00
find_prev - > set_disabled ( results_count < 1 ) ;
find_next - > set_disabled ( results_count < 1 ) ;
2017-12-22 16:09:31 -02:00
}
void FindBar : : _hide_bar ( ) {
2020-05-14 16:41:43 +02:00
if ( search_text - > has_focus ( ) ) {
2017-12-22 16:09:31 -02:00
rich_text_label - > grab_focus ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-12-22 16:09:31 -02:00
hide ( ) ;
}
2025-02-02 18:31:12 +01:00
// Implemented in input(..) as the LineEdit consumes the Escape pressed key.
void FindBar : : input ( const Ref < InputEvent > & p_event ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_event . is_null ( ) ) ;
2017-12-22 16:09:31 -02:00
Ref < InputEventKey > k = p_event ;
2023-05-20 17:26:13 -07:00
if ( k . is_valid ( ) & & k - > is_action_pressed ( SNAME ( " ui_cancel " ) , false , true ) ) {
2025-02-02 18:31:12 +01:00
Control * focus_owner = get_viewport ( ) - > gui_get_focus_owner ( ) ;
if ( rich_text_label - > has_focus ( ) | | ( focus_owner & & is_ancestor_of ( focus_owner ) ) ) {
2023-05-20 17:26:13 -07:00
_hide_bar ( ) ;
accept_event ( ) ;
2017-12-22 16:09:31 -02:00
}
}
}
void FindBar : : _search_text_changed ( const String & p_text ) {
search_next ( ) ;
}
2021-06-16 09:43:34 -07:00
void FindBar : : _search_text_submitted ( const String & p_text ) {
2021-08-13 16:31:57 -05:00
if ( Input : : get_singleton ( ) - > is_key_pressed ( Key : : SHIFT ) ) {
2019-09-08 15:09:52 +02:00
search_prev ( ) ;
} else {
search_next ( ) ;
}
2017-12-22 16:09:31 -02:00
}