From c6291bcd8a49055ce2158f88759a487de5b8d1bd Mon Sep 17 00:00:00 2001 From: Voylin <0voylin0@gmail.com> Date: Tue, 3 May 2022 08:29:38 +0900 Subject: [PATCH] Adding print_rich for printing with BBCode --- core/debugger/remote_debugger.cpp | 10 +- core/debugger/remote_debugger.h | 3 +- core/os/os.cpp | 15 +++ core/os/os.h | 1 + core/string/print_string.cpp | 95 ++++++++++++++++++- core/string/print_string.h | 12 ++- core/variant/variant_utility.cpp | 17 ++++ doc/classes/@GlobalScope.xml | 10 ++ editor/editor_log.cpp | 22 ++++- editor/editor_log.h | 1 + editor/editor_node.cpp | 10 +- editor/editor_node.h | 2 +- .../gdscript/tests/gdscript_test_runner.cpp | 2 +- modules/gdscript/tests/gdscript_test_runner.h | 2 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 24 +++++ modules/mono/glue/gd_glue.cpp | 22 +++++ 16 files changed, 232 insertions(+), 16 deletions(-) diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 5ee4e2c36875..508a71ece9fb 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -208,7 +208,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char * rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si); } -void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) { +void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) { RemoteDebugger *rd = static_cast(p_this); if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive prints during flush. @@ -237,7 +237,13 @@ void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p OutputString output_string; output_string.message = s; - output_string.type = p_error ? MESSAGE_TYPE_ERROR : MESSAGE_TYPE_LOG; + if (p_error) { + output_string.type = MESSAGE_TYPE_ERROR; + } else if (p_rich) { + output_string.type = MESSAGE_TYPE_LOG_RICH; + } else { + output_string.type = MESSAGE_TYPE_LOG; + } rd->output_strings.push_back(output_string); if (overflowed) { diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index fdb312ae6808..fe4bbe86ea8e 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -44,6 +44,7 @@ class RemoteDebugger : public EngineDebugger { enum MessageType { MESSAGE_TYPE_LOG, MESSAGE_TYPE_ERROR, + MESSAGE_TYPE_LOG_RICH, }; private: @@ -82,7 +83,7 @@ class RemoteDebugger : public EngineDebugger { Thread::ID flush_thread = 0; PrintHandlerList phl; - static void _print_handler(void *p_this, const String &p_string, bool p_error); + static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich); ErrorHandlerList eh; static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type); diff --git a/core/os/os.cpp b/core/os/os.cpp index 93477f4288f1..b9daf6fa5371 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -100,6 +100,21 @@ void OS::print(const char *p_format, ...) { va_end(argp); } +void OS::print_rich(const char *p_format, ...) { + if (!_stdout_enabled) { + return; + } + + va_list argp; + va_start(argp, p_format); + + if (_logger) { + _logger->logv(p_format, argp, false); + } + + va_end(argp); +} + void OS::printerr(const char *p_format, ...) { if (!_stderr_enabled) { return; diff --git a/core/os/os.h b/core/os/os.h index c6ea9d869a70..af6c38cbe030 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -120,6 +120,7 @@ class OS { void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, Logger::ErrorType p_type = Logger::ERR_ERROR); void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; + void print_rich(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; virtual String get_stdin_string(bool p_block = true) = 0; diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index 919c9e08e3c6..f58486e0a52b 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -79,7 +79,98 @@ void __print_line(String p_string) { _global_lock(); PrintHandlerList *l = print_handler_list; while (l) { - l->printfunc(l->userdata, p_string, false); + l->printfunc(l->userdata, p_string, false, false); + l = l->next; + } + + _global_unlock(); +} + +void __print_line_rich(String p_string) { + if (!_print_line_enabled) { + return; + } + + // Convert a subset of BBCode tags to ANSI escape codes for correct display in the terminal. + // Support of those ANSI escape codes varies across terminal emulators, + // especially for italic and strikethrough. + String p_string_ansi = p_string; + + p_string_ansi = p_string_ansi.replace("[b]", "\u001b[1m"); + p_string_ansi = p_string_ansi.replace("[/b]", "\u001b[22m"); + p_string_ansi = p_string_ansi.replace("[i]", "\u001b[3m"); + p_string_ansi = p_string_ansi.replace("[/i]", "\u001b[23m"); + p_string_ansi = p_string_ansi.replace("[u]", "\u001b[4m"); + p_string_ansi = p_string_ansi.replace("[/u]", "\u001b[24m"); + p_string_ansi = p_string_ansi.replace("[s]", "\u001b[9m"); + p_string_ansi = p_string_ansi.replace("[/s]", "\u001b[29m"); + + p_string_ansi = p_string_ansi.replace("[indent]", " "); + p_string_ansi = p_string_ansi.replace("[/indent]", ""); + p_string_ansi = p_string_ansi.replace("[code]", "\u001b[2m"); + p_string_ansi = p_string_ansi.replace("[/code]", "\u001b[22m"); + p_string_ansi = p_string_ansi.replace("[url]", ""); + p_string_ansi = p_string_ansi.replace("[/url]", ""); + p_string_ansi = p_string_ansi.replace("[center]", "\n\t\t\t"); + p_string_ansi = p_string_ansi.replace("[/center]", ""); + p_string_ansi = p_string_ansi.replace("[right]", "\n\t\t\t\t\t\t"); + p_string_ansi = p_string_ansi.replace("[/right]", ""); + + if (p_string_ansi.contains("[color")) { + p_string_ansi = p_string_ansi.replace("[color=black]", "\u001b[30m"); + p_string_ansi = p_string_ansi.replace("[color=red]", "\u001b[91m"); + p_string_ansi = p_string_ansi.replace("[color=green]", "\u001b[92m"); + p_string_ansi = p_string_ansi.replace("[color=lime]", "\u001b[92m"); + p_string_ansi = p_string_ansi.replace("[color=yellow]", "\u001b[93m"); + p_string_ansi = p_string_ansi.replace("[color=blue]", "\u001b[94m"); + p_string_ansi = p_string_ansi.replace("[color=magenta]", "\u001b[95m"); + p_string_ansi = p_string_ansi.replace("[color=pink]", "\u001b[38;5;218m"); + p_string_ansi = p_string_ansi.replace("[color=purple]", "\u001b[38;5;98m"); + p_string_ansi = p_string_ansi.replace("[color=cyan]", "\u001b[96m"); + p_string_ansi = p_string_ansi.replace("[color=white]", "\u001b[97m"); + p_string_ansi = p_string_ansi.replace("[color=orange]", "\u001b[38;5;208m"); + p_string_ansi = p_string_ansi.replace("[color=gray]", "\u001b[90m"); + p_string_ansi = p_string_ansi.replace("[/color]", "\u001b[39m"); + } + if (p_string_ansi.contains("[bgcolor")) { + p_string_ansi = p_string_ansi.replace("[bgcolor=black]", "\u001b[40m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=red]", "\u001b[101m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=green]", "\u001b[102m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=lime]", "\u001b[102m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=yellow]", "\u001b[103m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=blue]", "\u001b[104m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=magenta]", "\u001b[105m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=pink]", "\u001b[48;5;218m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=purple]", "\u001b[48;5;98m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=cyan]", "\u001b[106m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=white]", "\u001b[107m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=orange]", "\u001b[48;5;208m"); + p_string_ansi = p_string_ansi.replace("[bgcolor=gray]", "\u001b[100m"); + p_string_ansi = p_string_ansi.replace("[/bgcolor]", "\u001b[49m"); + } + if (p_string_ansi.contains("[fgcolor")) { + p_string_ansi = p_string_ansi.replace("[fgcolor=black]", "\u001b[30;40m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=red]", "\u001b[91;101m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=green]", "\u001b[92;102m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=lime]", "\u001b[92;102m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=yellow]", "\u001b[93;103m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=blue]", "\u001b[94;104m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=magenta]", "\u001b[95;105m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=pink]", "\u001b[38;5;218;48;5;218m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=purple]", "\u001b[38;5;98;48;5;98m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=cyan]", "\u001b[96;106m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=white]", "\u001b[97;107m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=orange]", "\u001b[38;5;208;48;5;208m"); + p_string_ansi = p_string_ansi.replace("[fgcolor=gray]", "\u001b[90;100m"); + p_string_ansi = p_string_ansi.replace("[/fgcolor]", "\u001b[39;49m"); + } + + OS::get_singleton()->print_rich("%s\n", p_string_ansi.utf8().get_data()); + + _global_lock(); + PrintHandlerList *l = print_handler_list; + while (l) { + l->printfunc(l->userdata, p_string, false, true); l = l->next; } @@ -96,7 +187,7 @@ void print_error(String p_string) { _global_lock(); PrintHandlerList *l = print_handler_list; while (l) { - l->printfunc(l->userdata, p_string, true); + l->printfunc(l->userdata, p_string, true, false); l = l->next; } diff --git a/core/string/print_string.h b/core/string/print_string.h index f7d0f2503087..823e2c29e8fb 100644 --- a/core/string/print_string.h +++ b/core/string/print_string.h @@ -35,7 +35,7 @@ extern void (*_print_func)(String); -typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error); +typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error, bool p_rich); struct PrintHandlerList { PrintHandlerFunc printfunc = nullptr; @@ -59,6 +59,7 @@ void remove_print_handler(const PrintHandlerList *p_handler); extern bool _print_line_enabled; extern bool _print_error_enabled; extern void __print_line(String p_string); +extern void __print_line_rich(String p_string); extern void print_error(String p_string); extern void print_verbose(String p_string); @@ -66,9 +67,18 @@ inline void print_line(Variant v) { __print_line(stringify_variants(v)); } +inline void print_line_rich(Variant v) { + __print_line_rich(stringify_variants(v)); +} + template void print_line(Variant p_var, Args... p_args) { __print_line(stringify_variants(p_var, p_args...)); } +template +void print_line_rich(Variant p_var, Args... p_args) { + __print_line_rich(stringify_variants(p_var, p_args...)); +} + #endif // PRINT_STRING_H diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index d1b5e285d25f..2bca5f828452 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -560,6 +560,22 @@ struct VariantUtilityFunctions { r_error.error = Callable::CallError::CALL_OK; } + static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; + } + } + + print_line_rich(s); + r_error.error = Callable::CallError::CALL_OK; + } + static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (OS::get_singleton()->is_stdout_verbose()) { String s; @@ -1306,6 +1322,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(print_rich, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printerr, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(prints, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 1a0253a28b67..194322130909 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -657,6 +657,16 @@ [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. + + + Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output. + When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough. + [codeblock] + print_rich("[code][b]Hello world![/b][/code]") # Prints out: [b]Hello world![/b] + [/codeblock] + [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print] or [method print_rich]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. + + If verbose mode is enabled ([method OS.is_stdout_verbose] returning [code]true[/code]), converts one or more arguments of any type to string in the best way possible and prints them to the console. diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index dbe44aee1b86..f26f47dbc7c4 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -181,7 +181,7 @@ void EditorLog::clear() { } void EditorLog::_process_message(const String &p_msg, MessageType p_type) { - if (messages.size() > 0 && messages[messages.size() - 1].text == p_msg) { + if (messages.size() > 0 && messages[messages.size() - 1].text == p_msg && messages[messages.size() - 1].type == p_type) { // If previous message is the same as the new one, increase previous count rather than adding another // instance to the messages list. LogMessage &previous = messages.write[messages.size() - 1]; @@ -258,6 +258,8 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { switch (p_message.type) { case MSG_TYPE_STD: { } break; + case MSG_TYPE_STD_RICH: { + } break; case MSG_TYPE_ERROR: { log->push_color(get_theme_color(SNAME("error_color"), SNAME("Editor"))); Ref icon = get_theme_icon(SNAME("Error"), SNAME("EditorIcons")); @@ -285,11 +287,15 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { log->pop(); } - log->add_text(p_message.text); + if (p_message.type == MSG_TYPE_STD_RICH) { + log->append_text(p_message.text); + } else { + log->add_text(p_message.text); + } // Need to use pop() to exit out of the RichTextLabels current "push" stack. - // We only "push" in the above switch when message type != STD, so only pop when that is the case. - if (p_message.type != MSG_TYPE_STD) { + // We only "push" in the above switch when message type != STD and RICH, so only pop when that is the case. + if (p_message.type != MSG_TYPE_STD && p_message.type != MSG_TYPE_STD_RICH) { log->pop(); } @@ -342,6 +348,7 @@ EditorLog::EditorLog() { // Log - Rich Text Label. log = memnew(RichTextLabel); + log->set_use_bbcode(true); log->set_scroll_follow(true); log->set_selection_enabled(true); log->set_focus_mode(FOCUS_CLICK); @@ -418,6 +425,7 @@ EditorLog::EditorLog() { std_filter->initialize_button(TTR("Toggle visibility of standard output messages."), callable_mp(this, &EditorLog::_set_filter_active)); vb_right->add_child(std_filter->toggle_button); type_filter_map.insert(MSG_TYPE_STD, std_filter); + type_filter_map.insert(MSG_TYPE_STD_RICH, std_filter); LogFilter *error_filter = memnew(LogFilter(MSG_TYPE_ERROR)); error_filter->initialize_button(TTR("Toggle visibility of errors."), callable_mp(this, &EditorLog::_set_filter_active)); @@ -451,6 +459,10 @@ void EditorLog::deinit() { EditorLog::~EditorLog() { for (const KeyValue &E : type_filter_map) { - memdelete(E.value); + // MSG_TYPE_STD_RICH is connected to the std_filter button, so we do this + // to avoid it from being deleted twice, causing a crash on closing. + if (E.key != MSG_TYPE_STD_RICH) { + memdelete(E.value); + } } } diff --git a/editor/editor_log.h b/editor/editor_log.h index de0368501c14..653fba9524b0 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -48,6 +48,7 @@ class EditorLog : public HBoxContainer { enum MessageType { MSG_TYPE_STD, MSG_TYPE_ERROR, + MSG_TYPE_STD_RICH, MSG_TYPE_WARNING, MSG_TYPE_EDITOR, }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b4b82b1edfaf..b196cadcb1f0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5829,9 +5829,15 @@ static Node *_resource_get_edited_scene() { return EditorNode::get_singleton()->get_edited_scene(); } -void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error) { +void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) { EditorNode *en = static_cast(p_this); - en->log->add_message(p_string, p_error ? EditorLog::MSG_TYPE_ERROR : EditorLog::MSG_TYPE_STD); + if (p_error) { + en->log->add_message(p_string, EditorLog::MSG_TYPE_ERROR); + } else if (p_rich) { + en->log->add_message(p_string, EditorLog::MSG_TYPE_STD_RICH); + } else { + en->log->add_message(p_string, EditorLog::MSG_TYPE_STD); + } } static void _execute_thread(void *p_ud) { diff --git a/editor/editor_node.h b/editor/editor_node.h index 89f80baeb9cb..c327a73ce938 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -505,7 +505,7 @@ class EditorNode : public Node { static void _load_error_notify(void *p_ud, const String &p_text); static void _file_access_close_error_notify(const String &p_str); - static void _print_handler(void *p_this, const String &p_string, bool p_error); + static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich); static void _resource_saved(Ref p_resource, const String &p_path); static void _resource_loaded(Ref p_resource, const String &p_path); diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index de5cd10e7c98..ff4832bde057 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -363,7 +363,7 @@ void GDScriptTest::disable_stdout() { OS::get_singleton()->set_stderr_enabled(false); } -void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error) { +void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error, bool p_rich) { TestResult *result = (TestResult *)p_this; result->output += p_message + "\n"; } diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index d6c6419e217d..ee21afd9c9ad 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -86,7 +86,7 @@ class GDScriptTest { TestResult execute_test_code(bool p_is_generating); public: - static void print_handler(void *p_this, const String &p_message, bool p_error); + static void print_handler(void *p_this, const String &p_message, bool p_error, bool p_rich); static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type); TestResult run_test(); bool generate_output(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 74aa38386f48..bb076a963381 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -238,6 +238,27 @@ public static void Print(params object[] what) godot_icall_GD_print(GetPrintParams(what)); } + /// + /// Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output. + /// When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough. + /// + /// Note: Consider using and + /// to print error and warning messages instead of or . + /// This distinguishes them from print messages used for debugging purposes, + /// while also displaying a stack trace when an error or warning is printed. + /// + /// + /// + /// GD.PrintRich("[b]Hello world![/b]"); // Prints out "Hello world!" in bold. + /// + /// + /// Arguments that will be printed. + /// + public static void PrintRich(params object[] what) + { + godot_icall_GD_print_rich(GetPrintParams(what)); + } + /// /// Prints the current stack trace information to the console. /// @@ -561,6 +582,9 @@ public static Variant.Type TypeToVariantType(Type type) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_GD_print(object[] what); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void godot_icall_GD_print_rich(object[] what); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_GD_printerr(object[] what); diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 8aead217cf69..8b1c2b729ea6 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -90,6 +90,27 @@ void godot_icall_GD_print(MonoArray *p_what) { print_line(str); } +void godot_icall_GD_print_rich(MonoArray *p_what) { + String str; + int length = mono_array_length(p_what); + + for (int i = 0; i < length; i++) { + MonoObject *elem = mono_array_get(p_what, MonoObject *, i); + + MonoException *exc = nullptr; + String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return; + } + + str += elem_str; + } + + print_line_rich(str); +} + void godot_icall_GD_printerr(MonoArray *p_what) { String str; int length = mono_array_length(p_what); @@ -300,6 +321,7 @@ void godot_register_gd_icalls() { GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror); GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning); GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print_rich", godot_icall_GD_print_rich); GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr); GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw); GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints);