mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-01-03 01:19:38 +01:00
Backported the new output window from godot4.
This commit is contained in:
parent
56b812f22f
commit
deb5c49700
@ -127,6 +127,18 @@
|
||||
Terminates the current tag. Use after [code]push_*[/code] methods to close BBCodes manually. Does not need to follow [code]add_*[/code] methods.
|
||||
</description>
|
||||
</method>
|
||||
<method name="pop_all">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Terminates all tags opened by [code]push_*[/code] methods.
|
||||
</description>
|
||||
</method>
|
||||
<method name="pop_context">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Terminates tags opened after the last [method push_context] call (including context marker), or all tags if there's no context marker on the stack.
|
||||
</description>
|
||||
</method>
|
||||
<method name="push_align">
|
||||
<return type="void" />
|
||||
<argument index="0" name="align" type="int" enum="RichTextLabel.Align" />
|
||||
@ -159,6 +171,12 @@
|
||||
Adds a [code][color][/code] tag to the tag stack.
|
||||
</description>
|
||||
</method>
|
||||
<method name="push_context">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Adds a context marker to the tag stack. See [method pop_context].
|
||||
</description>
|
||||
</method>
|
||||
<method name="push_font">
|
||||
<return type="void" />
|
||||
<argument index="0" name="font" type="Font" />
|
||||
|
@ -30,14 +30,14 @@
|
||||
|
||||
#include "editor_log.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/math/color.h"
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "core/object/undo_redo.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "core/object/undo_redo.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor_node.h"
|
||||
@ -45,17 +45,55 @@
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/tool_button.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/dynamic_font.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
void EditorLog::LogFilter::initialize_button(const String &p_tooltip, Object *p_cb_obj, const StringName &p_method) {
|
||||
toggle_button = memnew(Button);
|
||||
toggle_button->set_toggle_mode(true);
|
||||
toggle_button->set_pressed(true);
|
||||
toggle_button->set_text(itos(message_count));
|
||||
toggle_button->set_tooltip(TTR(p_tooltip));
|
||||
// Don't tint the icon even when in "pressed" state.
|
||||
toggle_button->add_theme_color_override("icon_color_pressed", Color(1, 1, 1, 1));
|
||||
toggle_button->set_focus_mode(FOCUS_NONE);
|
||||
// When toggled call the callback and pass the MessageType this button is for.
|
||||
toggle_button->connect("toggled", p_cb_obj, p_method, varray(type));
|
||||
}
|
||||
|
||||
int EditorLog::LogFilter::get_message_count() {
|
||||
return message_count;
|
||||
}
|
||||
|
||||
void EditorLog::LogFilter::set_message_count(int p_count) {
|
||||
message_count = p_count;
|
||||
toggle_button->set_text(itos(message_count));
|
||||
}
|
||||
|
||||
bool EditorLog::LogFilter::is_active() {
|
||||
return active;
|
||||
}
|
||||
|
||||
void EditorLog::LogFilter::set_active(bool p_active) {
|
||||
toggle_button->set_pressed(p_active);
|
||||
active = p_active;
|
||||
}
|
||||
|
||||
EditorLog::LogFilter::LogFilter(MessageType p_type) {
|
||||
message_count = 0;
|
||||
active = true;
|
||||
|
||||
type = p_type;
|
||||
toggle_button = NULL;
|
||||
}
|
||||
|
||||
void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) {
|
||||
EditorLog *self = (EditorLog *)p_self;
|
||||
if (self->current != Thread::get_caller_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String err_str;
|
||||
if (p_errorexp && p_errorexp[0]) {
|
||||
@ -69,37 +107,128 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
|
||||
} else {
|
||||
self->add_message(err_str, MSG_TYPE_ERROR);
|
||||
}
|
||||
|
||||
MessageType message_type = p_type == ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR;
|
||||
|
||||
if (self->current != Thread::get_caller_id()) {
|
||||
self->call_deferred("add_message", err_str, message_type);
|
||||
} else {
|
||||
self->add_message(err_str, message_type);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLog::_update_theme() {
|
||||
const Ref<Font> normal_font = get_theme_font(("output_source"), ("EditorFonts"));
|
||||
if (normal_font.is_valid()) {
|
||||
log->add_theme_font_override("normal_font", normal_font);
|
||||
}
|
||||
|
||||
const Ref<Font> bold_font = get_theme_font(("output_source_bold"), ("EditorFonts"));
|
||||
if (bold_font.is_valid()) {
|
||||
log->add_theme_font_override("bold_font", bold_font);
|
||||
}
|
||||
|
||||
const Ref<Font> italics_font = get_theme_font(("output_source_italic"), ("EditorFonts"));
|
||||
if (italics_font.is_valid()) {
|
||||
log->add_theme_font_override("italics_font", italics_font);
|
||||
}
|
||||
|
||||
const Ref<Font> bold_italics_font = get_theme_font(("output_source_bold_italic"), ("EditorFonts"));
|
||||
if (bold_italics_font.is_valid()) {
|
||||
log->add_theme_font_override("bold_italics_font", bold_italics_font);
|
||||
}
|
||||
|
||||
const Ref<Font> mono_font = get_theme_font(("output_source_mono"), ("EditorFonts"));
|
||||
if (mono_font.is_valid()) {
|
||||
log->add_theme_font_override("mono_font", mono_font);
|
||||
}
|
||||
|
||||
// Disable padding for highlighted background/foreground to prevent highlights from overlapping on close lines.
|
||||
// This also better matches terminal output, which does not use any form of padding.
|
||||
log->add_theme_constant_override("text_highlight_h_padding", 0);
|
||||
log->add_theme_constant_override("text_highlight_v_padding", 0);
|
||||
|
||||
//const int font_size = get_theme_font_size(("output_source_size"), ("EditorFonts"));
|
||||
//log->add_theme_font_size_override("normal_font_size", font_size);
|
||||
//log->add_theme_font_size_override("bold_font_size", font_size);
|
||||
//log->add_theme_font_size_override("italics_font_size", font_size);
|
||||
//log->add_theme_font_size_override("mono_font_size", font_size);
|
||||
|
||||
type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon(("Popup"), ("EditorIcons")));
|
||||
type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon(("StatusError"), ("EditorIcons")));
|
||||
type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon(("StatusWarning"), ("EditorIcons")));
|
||||
type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon(("Edit"), ("EditorIcons")));
|
||||
|
||||
type_filter_map[MSG_TYPE_STD]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
|
||||
clear_button->set_icon(get_theme_icon(("Clear"), ("EditorIcons")));
|
||||
copy_button->set_icon(get_theme_icon(("ActionCopy"), ("EditorIcons")));
|
||||
collapse_button->set_icon(get_theme_icon(("CombineLines"), ("EditorIcons")));
|
||||
show_search_button->set_icon(get_theme_icon(("Search"), ("EditorIcons")));
|
||||
search_box->set_right_icon(get_theme_icon(("Search"), ("EditorIcons")));
|
||||
|
||||
theme_cache.error_color = get_theme_color(("error_color"), ("Editor"));
|
||||
theme_cache.error_icon = get_theme_icon(("Error"), ("EditorIcons"));
|
||||
theme_cache.warning_color = get_theme_color(("warning_color"), ("Editor"));
|
||||
theme_cache.warning_icon = get_theme_icon(("Warning"), ("EditorIcons"));
|
||||
theme_cache.message_color = get_theme_color(("font_color"), ("Editor")) * Color(1, 1, 1, 0.6);
|
||||
}
|
||||
|
||||
void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
|
||||
EditorLog *self = (EditorLog *)p_self;
|
||||
self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
|
||||
}
|
||||
|
||||
void EditorLog::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_ENTER_TREE) {
|
||||
//button->set_icon(get_theme_icon("Console","EditorIcons"));
|
||||
log->add_theme_font_override("normal_font", get_theme_font("output_source", "EditorFonts"));
|
||||
log->add_theme_color_override("selection_color", get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
||||
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
|
||||
Ref<DynamicFont> df_output_code = get_theme_font("output_source", "EditorFonts");
|
||||
if (df_output_code.is_valid()) {
|
||||
if (log != nullptr) {
|
||||
log->add_theme_font_override("normal_font", get_theme_font("output_source", "EditorFonts"));
|
||||
log->add_theme_color_override("selection_color", get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
||||
switch (p_what) {
|
||||
case (NOTIFICATION_ENTER_TREE): {
|
||||
/*
|
||||
log->add_theme_font_override("normal_font", get_theme_font("output_source", "EditorFonts"));
|
||||
log->add_theme_color_override("selection_color", get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
||||
*/
|
||||
_update_theme();
|
||||
break;
|
||||
}
|
||||
case (NOTIFICATION_THEME_CHANGED): {
|
||||
/*
|
||||
Ref<DynamicFont> df_output_code = get_theme_font("output_source", "EditorFonts");
|
||||
if (df_output_code.is_valid()) {
|
||||
if (log != nullptr) {
|
||||
log->add_theme_font_override("normal_font", get_theme_font("output_source", "EditorFonts"));
|
||||
log->add_theme_color_override("selection_color", get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
||||
}
|
||||
}
|
||||
*/
|
||||
_update_theme();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLog::_set_collapse(bool p_collapse) {
|
||||
collapse = p_collapse;
|
||||
_rebuild_log();
|
||||
}
|
||||
|
||||
void EditorLog::_clear_request() {
|
||||
log->clear();
|
||||
messages.clear();
|
||||
_reset_message_counts();
|
||||
tool_button->set_icon(Ref<Texture>());
|
||||
}
|
||||
|
||||
void EditorLog::_copy_request() {
|
||||
String text = log->get_selected_text();
|
||||
|
||||
if (text == "") {
|
||||
if (text.empty()) {
|
||||
text = log->get_text();
|
||||
//get_parsed_text TODO
|
||||
}
|
||||
|
||||
if (text != "") {
|
||||
if (!text.empty()) {
|
||||
OS::get_singleton()->set_clipboard(text);
|
||||
}
|
||||
}
|
||||
@ -112,7 +241,26 @@ void EditorLog::copy() {
|
||||
_copy_request();
|
||||
}
|
||||
|
||||
void EditorLog::_process_message(const String &p_msg, MessageType p_type, bool p_clear) {
|
||||
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];
|
||||
previous.count++;
|
||||
|
||||
_add_log_line(previous, collapse);
|
||||
} else {
|
||||
// Different message to the previous one received.
|
||||
LogMessage message(p_msg, p_type, p_clear);
|
||||
_add_log_line(message);
|
||||
messages.push_back(message);
|
||||
}
|
||||
|
||||
type_filter_map[p_type]->set_message_count(type_filter_map[p_type]->get_message_count() + 1);
|
||||
}
|
||||
|
||||
void EditorLog::add_message(const String &p_msg, MessageType p_type) {
|
||||
/* TODO
|
||||
bool restore = p_type != MSG_TYPE_STD;
|
||||
switch (p_type) {
|
||||
case MSG_TYPE_STD: {
|
||||
@ -143,56 +291,248 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
|
||||
if (restore) {
|
||||
log->pop();
|
||||
}
|
||||
*/
|
||||
|
||||
// Make text split by new lines their own message.
|
||||
// See #41321 for reasoning. At time of writing, multiple print()'s in running projects
|
||||
// get grouped together and sent to the editor log as one message. This can mess with the
|
||||
// search functionality (see the comments on the PR above for more details). This behavior
|
||||
// also matches that of other IDE's.
|
||||
Vector<String> lines = p_msg.split("\n", true);
|
||||
int line_count = lines.size();
|
||||
|
||||
for (int i = 0; i < line_count; i++) {
|
||||
_process_message(lines[i], p_type, i == line_count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLog::set_tool_button(ToolButton *p_tool_button) {
|
||||
tool_button = p_tool_button;
|
||||
}
|
||||
|
||||
void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
|
||||
EditorLog *self = (EditorLog *)p_self;
|
||||
self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
|
||||
void EditorLog::_rebuild_log() {
|
||||
log->clear();
|
||||
|
||||
for (int msg_idx = 0; msg_idx < messages.size(); msg_idx++) {
|
||||
LogMessage msg = messages[msg_idx];
|
||||
|
||||
if (collapse) {
|
||||
// If collapsing, only log one instance of the message.
|
||||
_add_log_line(msg);
|
||||
} else {
|
||||
// If not collapsing, log each instance on a line.
|
||||
for (int i = 0; i < msg.count; i++) {
|
||||
_add_log_line(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
|
||||
if (!is_inside_tree()) {
|
||||
// The log will be built all at once when it enters the tree and has its theme items.
|
||||
return;
|
||||
}
|
||||
|
||||
// Only add the message to the log if it passes the filters.
|
||||
bool filter_active = type_filter_map[p_message.type]->is_active();
|
||||
String search_text = search_box->get_text();
|
||||
bool search_match = search_text.empty() || p_message.text.findn(search_text) > -1;
|
||||
|
||||
if (!filter_active || !search_match) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_replace_previous) {
|
||||
// Remove last line if replacing, as it will be replace by the next added line.
|
||||
// Why "- 2"? RichTextLabel is weird. When you add a line with add_newline(), it also adds an element to the list of lines which is null/blank,
|
||||
// but it still counts as a line. So if you remove the last line (count - 1) you are actually removing nothing...
|
||||
log->remove_line(log->get_line_count() - 2);
|
||||
}
|
||||
|
||||
switch (p_message.type) {
|
||||
case MSG_TYPE_STD: {
|
||||
} break;
|
||||
case MSG_TYPE_ERROR: {
|
||||
log->push_color(theme_cache.error_color);
|
||||
Ref<Texture> icon = theme_cache.error_icon;
|
||||
log->add_image(icon);
|
||||
log->add_text(" ");
|
||||
tool_button->set_icon(icon);
|
||||
} break;
|
||||
case MSG_TYPE_WARNING: {
|
||||
log->push_color(theme_cache.warning_color);
|
||||
Ref<Texture> icon = theme_cache.warning_icon;
|
||||
log->add_image(icon);
|
||||
log->add_text(" ");
|
||||
tool_button->set_icon(icon);
|
||||
} break;
|
||||
case MSG_TYPE_EDITOR: {
|
||||
// Distinguish editor messages from messages printed by the project
|
||||
log->push_color(theme_cache.message_color);
|
||||
} break;
|
||||
}
|
||||
|
||||
// If collapsing, add the count of this message in bold at the start of the line.
|
||||
if (collapse && p_message.count > 1) {
|
||||
log->push_bold();
|
||||
log->add_text(vformat("(%s) ", itos(p_message.count)));
|
||||
log->pop();
|
||||
}
|
||||
|
||||
log->add_text(p_message.text);
|
||||
|
||||
if (p_message.clear) {
|
||||
log->pop_all(); // Pop all unclosed tags.
|
||||
}
|
||||
|
||||
log->add_newline();
|
||||
}
|
||||
|
||||
void EditorLog::_set_filter_active(bool p_active, MessageType p_message_type) {
|
||||
type_filter_map[p_message_type]->set_active(p_active);
|
||||
_rebuild_log();
|
||||
}
|
||||
|
||||
void EditorLog::_set_search_visible(bool p_visible) {
|
||||
search_box->set_visible(p_visible);
|
||||
if (p_visible) {
|
||||
search_box->grab_focus();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLog::_search_changed(const String &p_text) {
|
||||
_rebuild_log();
|
||||
}
|
||||
|
||||
void EditorLog::_reset_message_counts() {
|
||||
for (HashMap<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next) {
|
||||
E->value()->set_message_count(0);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLog::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_clear_request"), &EditorLog::_clear_request);
|
||||
ClassDB::bind_method(D_METHOD("_copy_request"), &EditorLog::_copy_request);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_set_filter_active"), &EditorLog::_set_filter_active);
|
||||
ClassDB::bind_method(D_METHOD("_set_search_visible"), &EditorLog::_set_search_visible);
|
||||
ClassDB::bind_method(D_METHOD("_search_changed"), &EditorLog::_search_changed);
|
||||
ClassDB::bind_method(D_METHOD("_set_collapse"), &EditorLog::_set_collapse);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("clear_request"));
|
||||
ADD_SIGNAL(MethodInfo("copy_request"));
|
||||
}
|
||||
|
||||
EditorLog::EditorLog() {
|
||||
VBoxContainer *vb = this;
|
||||
collapse = false;
|
||||
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
vb->add_child(hb);
|
||||
title = memnew(Label);
|
||||
title->set_text(TTR("Output:"));
|
||||
title->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
hb->add_child(title);
|
||||
HBoxContainer *hb = this;
|
||||
|
||||
copybutton = memnew(Button);
|
||||
hb->add_child(copybutton);
|
||||
copybutton->set_text(TTR("Copy"));
|
||||
copybutton->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CMD | KEY_C));
|
||||
copybutton->connect("pressed", this, "_copy_request");
|
||||
|
||||
clearbutton = memnew(Button);
|
||||
hb->add_child(clearbutton);
|
||||
clearbutton->set_text(TTR("Clear"));
|
||||
clearbutton->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K));
|
||||
clearbutton->connect("pressed", this, "_clear_request");
|
||||
VBoxContainer *vb_left = memnew(VBoxContainer);
|
||||
vb_left->set_custom_minimum_size(Size2(0, 180) * EDSCALE);
|
||||
vb_left->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
vb_left->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
hb->add_child(vb_left);
|
||||
|
||||
// Log - Rich Text Label.
|
||||
log = memnew(RichTextLabel);
|
||||
//log->set_threaded(true);
|
||||
log->set_use_bbcode(true);
|
||||
log->set_scroll_follow(true);
|
||||
log->set_selection_enabled(true);
|
||||
//log->set_context_menu_enabled(true);
|
||||
log->set_focus_mode(FOCUS_CLICK);
|
||||
log->set_custom_minimum_size(Size2(0, 180) * EDSCALE);
|
||||
log->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
log->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
log->set_deselect_on_focus_loss_enabled(false);
|
||||
vb->add_child(log);
|
||||
add_message(VERSION_FULL_NAME " (c) 2007-2022 Juan Linietsky, Ariel Manzur & Godot Contributors.");
|
||||
vb_left->add_child(log);
|
||||
|
||||
// Search box
|
||||
search_box = memnew(LineEdit);
|
||||
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
search_box->set_placeholder(TTR("Filter Messages"));
|
||||
search_box->set_clear_button_enabled(true);
|
||||
search_box->set_visible(true);
|
||||
search_box->connect("text_changed", this, "_search_changed");
|
||||
vb_left->add_child(search_box);
|
||||
|
||||
VBoxContainer *vb_right = memnew(VBoxContainer);
|
||||
hb->add_child(vb_right);
|
||||
|
||||
// Tools grid
|
||||
HBoxContainer *hb_tools = memnew(HBoxContainer);
|
||||
hb_tools->set_h_size_flags(SIZE_SHRINK_CENTER);
|
||||
vb_right->add_child(hb_tools);
|
||||
|
||||
// Clear.
|
||||
clear_button = memnew(Button);
|
||||
clear_button->set_flat(true);
|
||||
clear_button->set_focus_mode(FOCUS_NONE);
|
||||
clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CTRL | KEY_MASK_SHIFT | KEY_K));
|
||||
//clear_button->set_shortcut_context(this);
|
||||
clear_button->connect("pressed", this, "_clear_request");
|
||||
hb_tools->add_child(clear_button);
|
||||
|
||||
// Copy.
|
||||
copy_button = memnew(Button);
|
||||
copy_button->set_flat(true);
|
||||
copy_button->set_focus_mode(FOCUS_NONE);
|
||||
copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CTRL | KEY_K));
|
||||
//copy_button->set_shortcut_context(this);
|
||||
copy_button->connect("pressed", this, "_copy_request");
|
||||
hb_tools->add_child(copy_button);
|
||||
|
||||
// A second hbox to make a 2x2 grid of buttons.
|
||||
HBoxContainer *hb_tools2 = memnew(HBoxContainer);
|
||||
hb_tools2->set_h_size_flags(SIZE_SHRINK_CENTER);
|
||||
vb_right->add_child(hb_tools2);
|
||||
|
||||
// Collapse.
|
||||
collapse_button = memnew(Button);
|
||||
collapse_button->set_flat(true);
|
||||
collapse_button->set_focus_mode(FOCUS_NONE);
|
||||
collapse_button->set_tooltip(TTR("Collapse duplicate messages into one log entry. Shows number of occurrences."));
|
||||
collapse_button->set_toggle_mode(true);
|
||||
collapse_button->set_pressed(false);
|
||||
collapse_button->connect("toggled", this, "_set_collapse");
|
||||
hb_tools2->add_child(collapse_button);
|
||||
|
||||
// Show Search.
|
||||
show_search_button = memnew(Button);
|
||||
show_search_button->set_flat(true);
|
||||
show_search_button->set_focus_mode(FOCUS_NONE);
|
||||
show_search_button->set_toggle_mode(true);
|
||||
show_search_button->set_pressed(true);
|
||||
show_search_button->set_shortcut(ED_SHORTCUT("editor/open_search", TTR("Focus Search/Filter Bar"), KEY_MASK_CTRL | KEY_F));
|
||||
//show_search_button->set_shortcut_context(this);
|
||||
show_search_button->connect("toggled", this, "_set_search_visible");
|
||||
hb_tools2->add_child(show_search_button);
|
||||
|
||||
// Message Type Filters.
|
||||
vb_right->add_child(memnew(HSeparator));
|
||||
|
||||
LogFilter *std_filter = memnew(LogFilter(MSG_TYPE_STD));
|
||||
std_filter->initialize_button(TTR("Toggle visibility of standard output messages."), this, "_set_filter_active");
|
||||
vb_right->add_child(std_filter->toggle_button);
|
||||
type_filter_map.insert(MSG_TYPE_STD, std_filter);
|
||||
|
||||
LogFilter *error_filter = memnew(LogFilter(MSG_TYPE_ERROR));
|
||||
error_filter->initialize_button(TTR("Toggle visibility of errors."), this, "_set_filter_active");
|
||||
vb_right->add_child(error_filter->toggle_button);
|
||||
type_filter_map.insert(MSG_TYPE_ERROR, error_filter);
|
||||
|
||||
LogFilter *warning_filter = memnew(LogFilter(MSG_TYPE_WARNING));
|
||||
warning_filter->initialize_button(TTR("Toggle visibility of warnings."), this, "_set_filter_active");
|
||||
vb_right->add_child(warning_filter->toggle_button);
|
||||
type_filter_map.insert(MSG_TYPE_WARNING, warning_filter);
|
||||
|
||||
LogFilter *editor_filter = memnew(LogFilter(MSG_TYPE_EDITOR));
|
||||
editor_filter->initialize_button(TTR("Toggle visibility of editor messages."), this, "_set_filter_active");
|
||||
vb_right->add_child(editor_filter->toggle_button);
|
||||
type_filter_map.insert(MSG_TYPE_EDITOR, editor_filter);
|
||||
|
||||
add_message(VERSION_FULL_NAME " (c) 2022-present Péter Magyar, 2007-2022 Juan Linietsky, Ariel Manzur & Godot Contributors.");
|
||||
|
||||
eh.errfunc = _error_handler;
|
||||
eh.userdata = this;
|
||||
@ -200,7 +540,7 @@ EditorLog::EditorLog() {
|
||||
|
||||
current = Thread::get_caller_id();
|
||||
|
||||
add_theme_constant_override("separation", get_theme_constant("separation", "VBoxContainer"));
|
||||
//add_theme_constant_override("separation", get_theme_constant("separation", "VBoxContainer"));
|
||||
|
||||
EditorNode::get_undo_redo()->set_commit_notify_callback(_undo_redo_cbk, this);
|
||||
}
|
||||
@ -210,4 +550,7 @@ void EditorLog::deinit() {
|
||||
}
|
||||
|
||||
EditorLog::~EditorLog() {
|
||||
for (HashMap<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next) {
|
||||
memdelete(E->value());
|
||||
}
|
||||
}
|
||||
|
@ -32,41 +32,19 @@
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
class Button;
|
||||
class Label;
|
||||
class RichTextLabel;
|
||||
class ToolButton;
|
||||
class LineEdit;
|
||||
|
||||
class EditorLog : public VBoxContainer {
|
||||
GDCLASS(EditorLog, VBoxContainer);
|
||||
|
||||
Button *clearbutton;
|
||||
Button *copybutton;
|
||||
Label *title;
|
||||
RichTextLabel *log;
|
||||
HBoxContainer *title_hb;
|
||||
//PaneDrag *pd;
|
||||
ToolButton *tool_button;
|
||||
|
||||
static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);
|
||||
|
||||
ErrorHandlerList eh;
|
||||
|
||||
Thread::ID current;
|
||||
|
||||
//void _dragged(const Point2& p_ofs);
|
||||
void _clear_request();
|
||||
void _copy_request();
|
||||
static void _undo_redo_cbk(void *p_self, const String &p_name);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
class EditorLog : public HBoxContainer {
|
||||
GDCLASS(EditorLog, HBoxContainer);
|
||||
|
||||
public:
|
||||
enum MessageType {
|
||||
@ -76,14 +54,115 @@ public:
|
||||
MSG_TYPE_EDITOR
|
||||
};
|
||||
|
||||
private:
|
||||
struct LogMessage {
|
||||
String text;
|
||||
MessageType type;
|
||||
int count = 1;
|
||||
bool clear = true;
|
||||
|
||||
LogMessage() {}
|
||||
|
||||
LogMessage(const String p_text, MessageType p_type, bool p_clear) :
|
||||
text(p_text),
|
||||
type(p_type),
|
||||
clear(p_clear) {
|
||||
}
|
||||
};
|
||||
|
||||
struct {
|
||||
Color error_color;
|
||||
Ref<Texture> error_icon;
|
||||
|
||||
Color warning_color;
|
||||
Ref<Texture> warning_icon;
|
||||
|
||||
Color message_color;
|
||||
} theme_cache;
|
||||
|
||||
// Encapsulates all data and functionality regarding filters.
|
||||
struct LogFilter {
|
||||
private:
|
||||
// Force usage of set method since it has functionality built-in.
|
||||
int message_count;
|
||||
bool active;
|
||||
|
||||
public:
|
||||
MessageType type;
|
||||
Button *toggle_button;
|
||||
|
||||
void initialize_button(const String &p_tooltip, Object *p_cb_obj, const StringName &p_method);
|
||||
|
||||
int get_message_count();
|
||||
void set_message_count(int p_count);
|
||||
|
||||
bool is_active();
|
||||
void set_active(bool p_active);
|
||||
|
||||
LogFilter(MessageType p_type);
|
||||
};
|
||||
|
||||
private:
|
||||
Vector<LogMessage> messages;
|
||||
// Maps MessageTypes to LogFilters for convenient access and storage (don't need 1 member per filter).
|
||||
HashMap<MessageType, LogFilter *> type_filter_map;
|
||||
|
||||
RichTextLabel *log;
|
||||
|
||||
Button *clear_button;
|
||||
Button *copy_button;
|
||||
|
||||
Button *collapse_button;
|
||||
bool collapse;
|
||||
|
||||
Button *show_search_button;
|
||||
LineEdit *search_box;
|
||||
|
||||
// Reference to the "Output" button on the toolbar so we can update it's icon when
|
||||
// Warnings or Errors are encounetered.
|
||||
ToolButton *tool_button;
|
||||
|
||||
static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);
|
||||
|
||||
ErrorHandlerList eh;
|
||||
|
||||
Thread::ID current;
|
||||
|
||||
void _clear_request();
|
||||
void _copy_request();
|
||||
|
||||
void _rebuild_log();
|
||||
void _add_log_line(LogMessage &p_message, bool p_replace_previous = false);
|
||||
|
||||
void _set_filter_active(bool p_active, MessageType p_message_type);
|
||||
void _set_search_visible(bool p_visible);
|
||||
void _search_changed(const String &p_text);
|
||||
|
||||
void _process_message(const String &p_msg, MessageType p_type, bool p_clear);
|
||||
void _reset_message_counts();
|
||||
|
||||
void _set_collapse(bool p_collapse);
|
||||
|
||||
void _update_theme();
|
||||
|
||||
static void _undo_redo_cbk(void *p_self, const String &p_name);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD);
|
||||
void set_tool_button(ToolButton *p_tool_button);
|
||||
void deinit();
|
||||
|
||||
void clear();
|
||||
void copy();
|
||||
|
||||
EditorLog();
|
||||
~EditorLog();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(EditorLog::MessageType);
|
||||
|
||||
#endif // EDITOR_LOG_H
|
||||
|
1
editor/icons/icon_combine_lines.svg
Normal file
1
editor/icons/icon_combine_lines.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 2v2h14V2zm7 5v2h7V7zm0 5v2h7v-2zM4.976 14V9h2l-1.5-2-1.5-2-1.5 2-1.5 2h2v5z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 195 B |
@ -1952,6 +1952,7 @@ void RichTextLabel::push_table(int p_columns) {
|
||||
}
|
||||
|
||||
void RichTextLabel::push_fade(int p_start_index, int p_length) {
|
||||
ERR_FAIL_COND(current->type == ITEM_TABLE);
|
||||
ItemFade *item = memnew(ItemFade);
|
||||
item->starting_index = p_start_index;
|
||||
item->length = p_length;
|
||||
@ -1959,6 +1960,7 @@ void RichTextLabel::push_fade(int p_start_index, int p_length) {
|
||||
}
|
||||
|
||||
void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
|
||||
ERR_FAIL_COND(current->type == ITEM_TABLE);
|
||||
ItemShake *item = memnew(ItemShake);
|
||||
item->strength = p_strength;
|
||||
item->rate = p_rate;
|
||||
@ -1966,6 +1968,7 @@ void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
|
||||
}
|
||||
|
||||
void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
|
||||
ERR_FAIL_COND(current->type == ITEM_TABLE);
|
||||
ItemWave *item = memnew(ItemWave);
|
||||
item->frequency = p_frequency;
|
||||
item->amplitude = p_amplitude;
|
||||
@ -1973,6 +1976,7 @@ void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0
|
||||
}
|
||||
|
||||
void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
|
||||
ERR_FAIL_COND(current->type == ITEM_TABLE);
|
||||
ItemTornado *item = memnew(ItemTornado);
|
||||
item->frequency = p_frequency;
|
||||
item->radius = p_radius;
|
||||
@ -1988,12 +1992,19 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq
|
||||
}
|
||||
|
||||
void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) {
|
||||
ERR_FAIL_COND(current->type == ITEM_TABLE);
|
||||
ItemCustomFX *item = memnew(ItemCustomFX);
|
||||
item->custom_effect = p_custom_effect;
|
||||
item->char_fx_transform->environment = p_environment;
|
||||
_add_item(item, true);
|
||||
}
|
||||
|
||||
void RichTextLabel::push_context() {
|
||||
ERR_FAIL_COND(current->type == ITEM_TABLE);
|
||||
ItemContext *item = memnew(ItemContext);
|
||||
_add_item(item, true);
|
||||
}
|
||||
|
||||
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
|
||||
ERR_FAIL_COND(current->type != ITEM_TABLE);
|
||||
ItemTable *table = static_cast<ItemTable *>(current);
|
||||
@ -2032,6 +2043,25 @@ void RichTextLabel::pop() {
|
||||
current = current->parent;
|
||||
}
|
||||
|
||||
void RichTextLabel::pop_context() {
|
||||
ERR_FAIL_NULL(current->parent);
|
||||
|
||||
while (current->parent && current != main) {
|
||||
if (current->type == ITEM_FRAME) {
|
||||
current_frame = static_cast<ItemFrame *>(current)->parent_frame;
|
||||
} else if (current->type == ITEM_CONTEXT) {
|
||||
current = current->parent;
|
||||
return;
|
||||
}
|
||||
current = current->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void RichTextLabel::pop_all() {
|
||||
current = main;
|
||||
current_frame = main;
|
||||
}
|
||||
|
||||
void RichTextLabel::clear() {
|
||||
main->_clear_children();
|
||||
current = main;
|
||||
@ -2861,7 +2891,11 @@ void RichTextLabel::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("push_table", "columns"), &RichTextLabel::push_table);
|
||||
ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
|
||||
ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell);
|
||||
ClassDB::bind_method(D_METHOD("push_customfx", "effect", "env"), &RichTextLabel::push_customfx);
|
||||
ClassDB::bind_method(D_METHOD("push_context"), &RichTextLabel::push_context);
|
||||
ClassDB::bind_method(D_METHOD("pop_context"), &RichTextLabel::pop_context);
|
||||
ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop);
|
||||
ClassDB::bind_method(D_METHOD("pop_all"), &RichTextLabel::pop_all);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear);
|
||||
ClassDB::bind_method(D_METHOD("deselect"), &RichTextLabel::deselect);
|
||||
|
@ -83,7 +83,8 @@ public:
|
||||
ITEM_TORNADO,
|
||||
ITEM_RAINBOW,
|
||||
ITEM_META,
|
||||
ITEM_CUSTOMFX
|
||||
ITEM_CUSTOMFX,
|
||||
ITEM_CONTEXT
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -317,6 +318,10 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
struct ItemContext : public Item {
|
||||
ItemContext() { type = ITEM_CONTEXT; }
|
||||
};
|
||||
|
||||
ItemFrame *main;
|
||||
Item *current;
|
||||
ItemFrame *current_frame;
|
||||
@ -446,10 +451,13 @@ public:
|
||||
void push_tornado(float p_frequency, float p_radius);
|
||||
void push_rainbow(float p_saturation, float p_value, float p_frequency);
|
||||
void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment);
|
||||
void push_context();
|
||||
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
|
||||
int get_current_table_column() const;
|
||||
void push_cell();
|
||||
void pop();
|
||||
void pop_context();
|
||||
void pop_all();
|
||||
|
||||
void clear();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user