2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* editor_log.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-10 02:10:30 +01:00
# include "editor_log.h"
2017-01-16 08:04:19 +01:00
2022-08-29 12:10:32 +02:00
# include "core/object/undo_redo.h"
2017-12-26 11:41:38 +01:00
# include "core/os/keyboard.h"
2018-09-11 18:13:45 +02:00
# include "core/version.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_node.h"
2022-07-29 02:36:26 +02:00
# include "editor/editor_paths.h"
2022-07-31 20:14:15 +02:00
# include "editor/editor_settings.h"
2023-08-13 02:33:39 +02:00
# include "editor/editor_string_names.h"
2024-01-15 13:14:55 +01:00
# include "editor/themes/editor_scale.h"
2017-03-05 16:44:50 +01:00
# include "scene/gui/center_container.h"
2022-07-31 20:14:15 +02:00
# include "scene/gui/separator.h"
2020-09-03 13:22:16 +02:00
# include "scene/resources/font.h"
2014-02-10 02:10:30 +01:00
2021-09-22 17:36:40 +02:00
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 , bool p_editor_notify , ErrorHandlerType p_type ) {
2022-04-05 12:40:26 +02:00
EditorLog * self = static_cast < EditorLog * > ( p_self ) ;
2014-02-10 02:10:30 +01:00
String err_str ;
if ( p_errorexp & & p_errorexp [ 0 ] ) {
2021-11-08 22:42:32 +01:00
err_str = String : : utf8 ( p_errorexp ) ;
2014-02-10 02:10:30 +01:00
} else {
2021-11-08 22:42:32 +01:00
err_str = String : : utf8 ( p_file ) + " : " + itos ( p_line ) + " - " + String : : utf8 ( p_error ) ;
2014-02-10 02:10:30 +01:00
}
2021-09-22 17:36:40 +02:00
if ( p_editor_notify ) {
err_str + = " (User) " ;
}
2023-05-15 01:58:27 +02:00
MessageType message_type = p_type = = ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR ;
if ( self - > current ! = Thread : : get_caller_id ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp ( self , & EditorLog : : add_message ) . call_deferred ( err_str , message_type ) ;
2018-08-13 17:04:38 +02:00
} else {
2023-05-15 01:58:27 +02:00
self - > add_message ( err_str , message_type ) ;
2018-08-13 17:04:38 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-08-29 11:04:31 +02:00
void EditorLog : : _update_theme ( ) {
2023-08-13 02:33:39 +02:00
const Ref < Font > normal_font = get_theme_font ( SNAME ( " output_source " ) , EditorStringName ( EditorFonts ) ) ;
2022-08-29 11:04:31 +02:00
if ( normal_font . is_valid ( ) ) {
log - > add_theme_font_override ( " normal_font " , normal_font ) ;
}
2023-08-13 02:33:39 +02:00
const Ref < Font > bold_font = get_theme_font ( SNAME ( " output_source_bold " ) , EditorStringName ( EditorFonts ) ) ;
2022-08-29 11:04:31 +02:00
if ( bold_font . is_valid ( ) ) {
log - > add_theme_font_override ( " bold_font " , bold_font ) ;
}
2023-08-13 02:33:39 +02:00
const Ref < Font > italics_font = get_theme_font ( SNAME ( " output_source_italic " ) , EditorStringName ( EditorFonts ) ) ;
2022-07-09 19:16:48 +02:00
if ( italics_font . is_valid ( ) ) {
log - > add_theme_font_override ( " italics_font " , italics_font ) ;
}
2023-08-13 02:33:39 +02:00
const Ref < Font > bold_italics_font = get_theme_font ( SNAME ( " output_source_bold_italic " ) , EditorStringName ( EditorFonts ) ) ;
2022-07-09 19:16:48 +02:00
if ( bold_italics_font . is_valid ( ) ) {
log - > add_theme_font_override ( " bold_italics_font " , bold_italics_font ) ;
}
2023-08-13 02:33:39 +02:00
const Ref < Font > mono_font = get_theme_font ( SNAME ( " output_source_mono " ) , EditorStringName ( EditorFonts ) ) ;
2022-07-09 19:16:48 +02:00
if ( mono_font . is_valid ( ) ) {
log - > add_theme_font_override ( " mono_font " , mono_font ) ;
}
2023-05-21 00:05:02 +02:00
// 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 ) ;
2023-08-13 02:33:39 +02:00
const int font_size = get_theme_font_size ( SNAME ( " output_source_size " ) , EditorStringName ( EditorFonts ) ) ;
2023-10-19 18:05:19 +02:00
log - > begin_bulk_theme_override ( ) ;
2023-04-07 14:01:55 +02:00
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 ) ;
2023-10-19 18:05:19 +02:00
log - > end_bulk_theme_override ( ) ;
2023-04-07 14:01:55 +02:00
2023-08-13 02:33:39 +02:00
type_filter_map [ MSG_TYPE_STD ] - > toggle_button - > set_icon ( get_editor_theme_icon ( SNAME ( " Popup " ) ) ) ;
type_filter_map [ MSG_TYPE_ERROR ] - > toggle_button - > set_icon ( get_editor_theme_icon ( SNAME ( " StatusError " ) ) ) ;
type_filter_map [ MSG_TYPE_WARNING ] - > toggle_button - > set_icon ( get_editor_theme_icon ( SNAME ( " StatusWarning " ) ) ) ;
type_filter_map [ MSG_TYPE_EDITOR ] - > toggle_button - > set_icon ( get_editor_theme_icon ( SNAME ( " Edit " ) ) ) ;
2022-08-29 11:04:31 +02:00
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 " ) ;
2023-08-13 02:33:39 +02:00
clear_button - > set_icon ( get_editor_theme_icon ( SNAME ( " Clear " ) ) ) ;
copy_button - > set_icon ( get_editor_theme_icon ( SNAME ( " ActionCopy " ) ) ) ;
collapse_button - > set_icon ( get_editor_theme_icon ( SNAME ( " CombineLines " ) ) ) ;
show_search_button - > set_icon ( get_editor_theme_icon ( SNAME ( " Search " ) ) ) ;
search_box - > set_right_icon ( get_editor_theme_icon ( SNAME ( " Search " ) ) ) ;
theme_cache . error_color = get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ;
theme_cache . error_icon = get_editor_theme_icon ( SNAME ( " Error " ) ) ;
theme_cache . warning_color = get_theme_color ( SNAME ( " warning_color " ) , EditorStringName ( Editor ) ) ;
theme_cache . warning_icon = get_editor_theme_icon ( SNAME ( " Warning " ) ) ;
theme_cache . message_color = get_theme_color ( SNAME ( " font_color " ) , EditorStringName ( Editor ) ) * Color ( 1 , 1 , 1 , 0.6 ) ;
2022-08-29 11:04:31 +02:00
}
2023-11-20 13:06:54 +01:00
void EditorLog : : _editor_settings_changed ( ) {
int new_line_limit = int ( EDITOR_GET ( " run/output/max_lines " ) ) ;
if ( new_line_limit ! = line_limit ) {
line_limit = new_line_limit ;
_rebuild_log ( ) ;
}
}
2014-02-10 02:10:30 +01:00
void EditorLog : : _notification ( int p_what ) {
2021-07-21 09:31:33 +02:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
2022-08-29 11:04:31 +02:00
_update_theme ( ) ;
2021-07-21 09:31:33 +02:00
_load_state ( ) ;
} break ;
2022-02-16 00:52:32 +01:00
2021-07-21 09:31:33 +02:00
case NOTIFICATION_THEME_CHANGED : {
2022-08-29 11:04:31 +02:00
_update_theme ( ) ;
2021-07-21 09:31:33 +02:00
_rebuild_log ( ) ;
} break ;
2014-02-10 02:10:30 +01:00
}
}
2020-08-16 09:30:52 +02:00
void EditorLog : : _set_collapse ( bool p_collapse ) {
collapse = p_collapse ;
2021-05-05 03:05:09 +02:00
_start_state_save_timer ( ) ;
2020-08-16 09:30:52 +02:00
_rebuild_log ( ) ;
}
2021-05-05 03:05:09 +02:00
void EditorLog : : _start_state_save_timer ( ) {
if ( ! is_loading_state ) {
save_state_timer - > start ( ) ;
}
}
void EditorLog : : _save_state ( ) {
Ref < ConfigFile > config ;
2021-06-18 00:03:09 +02:00
config . instantiate ( ) ;
2021-05-05 03:05:09 +02:00
// Load and amend existing config if it exists.
2022-08-30 02:34:01 +02:00
config - > load ( EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " editor_layout.cfg " ) ) ;
2021-05-05 03:05:09 +02:00
const String section = " editor_log " ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < MessageType , LogFilter * > & E : type_filter_map ) {
config - > set_value ( section , " log_filter_ " + itos ( E . key ) , E . value - > is_active ( ) ) ;
2021-05-05 03:05:09 +02:00
}
config - > set_value ( section , " collapse " , collapse ) ;
config - > set_value ( section , " show_search " , search_box - > is_visible ( ) ) ;
2022-08-30 02:34:01 +02:00
config - > save ( EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " editor_layout.cfg " ) ) ;
2021-05-05 03:05:09 +02:00
}
void EditorLog : : _load_state ( ) {
is_loading_state = true ;
Ref < ConfigFile > config ;
2021-06-18 00:03:09 +02:00
config . instantiate ( ) ;
2022-08-30 02:34:01 +02:00
config - > load ( EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " editor_layout.cfg " ) ) ;
2021-05-05 03:05:09 +02:00
2021-06-29 04:21:45 +02:00
// Run the below code even if config->load returns an error, since we want the defaults to be set even if the file does not exist yet.
const String section = " editor_log " ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < MessageType , LogFilter * > & E : type_filter_map ) {
E . value - > set_active ( config - > get_value ( section , " log_filter_ " + itos ( E . key ) , true ) ) ;
2021-05-05 03:05:09 +02:00
}
2021-06-29 04:21:45 +02:00
collapse = config - > get_value ( section , " collapse " , false ) ;
collapse_button - > set_pressed ( collapse ) ;
bool show_search = config - > get_value ( section , " show_search " , true ) ;
search_box - > set_visible ( show_search ) ;
show_search_button - > set_pressed ( show_search ) ;
2021-05-05 03:05:09 +02:00
is_loading_state = false ;
}
2024-01-15 14:29:52 +01:00
void EditorLog : : _meta_clicked ( const String & p_meta ) {
OS : : get_singleton ( ) - > shell_open ( p_meta ) ;
}
2015-07-29 00:41:24 +02:00
void EditorLog : : _clear_request ( ) {
log - > clear ( ) ;
2020-08-16 09:30:52 +02:00
messages . clear ( ) ;
_reset_message_counts ( ) ;
2019-06-11 20:43:37 +02:00
tool_button - > set_icon ( Ref < Texture2D > ( ) ) ;
2015-07-30 00:03:25 +02:00
}
2019-04-14 10:55:31 +02:00
void EditorLog : : _copy_request ( ) {
2020-09-14 20:14:27 +02:00
String text = log - > get_selected_text ( ) ;
2021-12-09 10:42:46 +01:00
if ( text . is_empty ( ) ) {
2022-04-08 22:13:48 +02:00
text = log - > get_parsed_text ( ) ;
2020-09-14 20:14:27 +02:00
}
2021-12-09 10:42:46 +01:00
if ( ! text . is_empty ( ) ) {
2020-09-14 20:14:27 +02:00
DisplayServer : : get_singleton ( ) - > clipboard_set ( text ) ;
}
2019-04-14 10:55:31 +02:00
}
2015-07-30 00:03:25 +02:00
void EditorLog : : clear ( ) {
_clear_request ( ) ;
2015-07-29 00:41:24 +02:00
}
2023-08-10 08:48:53 +02:00
void EditorLog : : _process_message ( const String & p_msg , MessageType p_type , bool p_clear ) {
2022-05-03 01:29:38 +02:00
if ( messages . size ( ) > 0 & & messages [ messages . size ( ) - 1 ] . text = = p_msg & & messages [ messages . size ( ) - 1 ] . type = = p_type ) {
2020-08-16 09:30:52 +02:00
// 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 + + ;
2020-08-17 07:52:42 +02:00
_add_log_line ( previous , collapse ) ;
2020-08-16 09:30:52 +02:00
} else {
// Different message to the previous one received.
2023-08-10 08:48:53 +02:00
LogMessage message ( p_msg , p_type , p_clear ) ;
2020-08-16 09:30:52 +02:00
_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 ) ;
2019-04-14 10:55:31 +02:00
}
2018-08-13 17:04:38 +02:00
void EditorLog : : add_message ( const String & p_msg , MessageType p_type ) {
2020-08-16 09:30:52 +02:00
// 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
2022-11-01 15:29:38 +01:00
// search functionality (see the comments on the PR above for more details). This behavior
2020-08-16 09:30:52 +02:00
// also matches that of other IDE's.
2021-08-29 11:35:13 +02:00
Vector < String > lines = p_msg . split ( " \n " , true ) ;
2023-08-10 08:48:53 +02:00
int line_count = lines . size ( ) ;
2020-08-16 09:30:52 +02:00
2023-08-10 08:48:53 +02:00
for ( int i = 0 ; i < line_count ; i + + ) {
_process_message ( lines [ i ] , p_type , i = = line_count - 1 ) ;
2020-08-16 09:30:52 +02:00
}
}
void EditorLog : : set_tool_button ( Button * p_tool_button ) {
tool_button = p_tool_button ;
}
2022-03-25 18:06:46 +01:00
void EditorLog : : register_undo_redo ( UndoRedo * p_undo_redo ) {
p_undo_redo - > set_commit_notify_callback ( _undo_redo_cbk , this ) ;
}
2020-08-16 09:30:52 +02:00
void EditorLog : : _undo_redo_cbk ( void * p_self , const String & p_name ) {
2022-04-05 12:40:26 +02:00
EditorLog * self = static_cast < EditorLog * > ( p_self ) ;
2020-08-16 09:30:52 +02:00
self - > add_message ( p_name , EditorLog : : MSG_TYPE_EDITOR ) ;
}
void EditorLog : : _rebuild_log ( ) {
2024-05-19 19:55:43 +02:00
if ( messages . is_empty ( ) ) {
return ;
}
2020-08-16 09:30:52 +02:00
log - > clear ( ) ;
2023-11-20 13:06:54 +01:00
int line_count = 0 ;
int start_message_index = 0 ;
int initial_skip = 0 ;
// Search backward for starting place.
for ( start_message_index = messages . size ( ) - 1 ; start_message_index > = 0 ; start_message_index - - ) {
LogMessage msg = messages [ start_message_index ] ;
if ( collapse ) {
if ( _check_display_message ( msg ) ) {
line_count + + ;
}
} else {
// If not collapsing, log each instance on a line.
for ( int i = 0 ; i < msg . count ; i + + ) {
if ( _check_display_message ( msg ) ) {
line_count + + ;
}
}
}
if ( line_count > = line_limit ) {
initial_skip = line_count - line_limit ;
break ;
}
if ( start_message_index = = 0 ) {
break ;
}
}
for ( int msg_idx = start_message_index ; msg_idx < messages . size ( ) ; msg_idx + + ) {
2020-08-16 09:30:52 +02:00
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.
2023-11-20 13:06:54 +01:00
for ( int i = initial_skip ; i < msg . count ; i + + ) {
initial_skip = 0 ;
2020-08-16 09:30:52 +02:00
_add_log_line ( msg ) ;
}
}
}
}
2023-11-20 13:06:54 +01:00
bool EditorLog : : _check_display_message ( LogMessage & p_message ) {
bool filter_active = type_filter_map [ p_message . type ] - > is_active ( ) ;
String search_text = search_box - > get_text ( ) ;
2024-05-06 10:26:10 +02:00
bool search_match = search_text . is_empty ( ) | | p_message . text . containsn ( search_text ) ;
2023-11-20 13:06:54 +01:00
return filter_active & & search_match ;
}
2020-08-16 09:30:52 +02:00
void EditorLog : : _add_log_line ( LogMessage & p_message , bool p_replace_previous ) {
2022-07-08 21:29:36 +02:00
if ( ! is_inside_tree ( ) ) {
// The log will be built all at once when it enters the tree and has its theme items.
return ;
}
2023-04-12 10:39:05 +02:00
if ( unlikely ( log - > is_updating ( ) ) ) {
// The new message arrived during log RTL text processing/redraw (invalid BiDi control characters / font error), ignore it to avoid RTL data corruption.
return ;
}
2020-08-16 09:30:52 +02:00
// Only add the message to the log if it passes the filters.
2023-11-20 13:06:54 +01:00
if ( ! _check_display_message ( p_message ) ) {
2020-08-16 09:30:52 +02:00
return ;
}
if ( p_replace_previous ) {
// Remove last line if replacing, as it will be replace by the next added line.
2021-05-25 13:48:28 +02:00
// 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,
2021-05-22 16:14:56 +02:00
// but it still counts as a line. So if you remove the last line (count - 1) you are actually removing nothing...
2022-12-21 18:54:18 +01:00
log - > remove_paragraph ( log - > get_paragraph_count ( ) - 2 ) ;
2020-08-16 09:30:52 +02:00
}
switch ( p_message . type ) {
2018-09-26 13:13:56 +02:00
case MSG_TYPE_STD : {
} break ;
2022-05-03 01:29:38 +02:00
case MSG_TYPE_STD_RICH : {
} break ;
2018-08-13 17:04:38 +02:00
case MSG_TYPE_ERROR : {
2022-08-11 12:48:43 +02:00
log - > push_color ( theme_cache . error_color ) ;
Ref < Texture2D > icon = theme_cache . error_icon ;
2018-08-13 17:04:38 +02:00
log - > add_image ( icon ) ;
log - > add_text ( " " ) ;
tool_button - > set_icon ( icon ) ;
} break ;
case MSG_TYPE_WARNING : {
2022-08-11 12:48:43 +02:00
log - > push_color ( theme_cache . warning_color ) ;
Ref < Texture2D > icon = theme_cache . warning_icon ;
2018-08-13 17:04:38 +02:00
log - > add_image ( icon ) ;
log - > add_text ( " " ) ;
tool_button - > set_icon ( icon ) ;
} break ;
2019-09-20 01:56:09 +02:00
case MSG_TYPE_EDITOR : {
// Distinguish editor messages from messages printed by the project
2022-08-11 12:48:43 +02:00
log - > push_color ( theme_cache . message_color ) ;
2019-09-20 01:56:09 +02:00
} break ;
2014-02-10 02:10:30 +01:00
}
2020-08-16 09:30:52 +02:00
// 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 ( ) ;
}
2022-05-03 01:29:38 +02:00
if ( p_message . type = = MSG_TYPE_STD_RICH ) {
log - > append_text ( p_message . text ) ;
} else {
log - > add_text ( p_message . text ) ;
}
2023-08-10 08:48:53 +02:00
if ( p_message . clear | | p_message . type ! = MSG_TYPE_STD_RICH ) {
log - > pop_all ( ) ; // Pop all unclosed tags.
}
2021-05-25 13:48:28 +02:00
log - > add_newline ( ) ;
2023-06-07 19:09:09 +02:00
if ( p_replace_previous ) {
// Force sync last line update (skip if number of unprocessed log messages is too large to avoid editor lag).
if ( log - > get_pending_paragraphs ( ) < 100 ) {
while ( ! log - > is_ready ( ) ) {
: : OS : : get_singleton ( ) - > delay_usec ( 1 ) ;
}
}
}
2023-11-20 13:06:54 +01:00
while ( log - > get_paragraph_count ( ) > line_limit + 1 ) {
log - > remove_paragraph ( 0 , true ) ;
}
2014-02-10 02:10:30 +01:00
}
2020-08-16 09:30:52 +02:00
void EditorLog : : _set_filter_active ( bool p_active , MessageType p_message_type ) {
2021-05-05 03:05:09 +02:00
type_filter_map [ p_message_type ] - > set_active ( p_active ) ;
_start_state_save_timer ( ) ;
2020-08-16 09:30:52 +02:00
_rebuild_log ( ) ;
2018-03-11 14:20:32 +01:00
}
2020-08-16 09:30:52 +02:00
void EditorLog : : _set_search_visible ( bool p_visible ) {
search_box - > set_visible ( p_visible ) ;
if ( p_visible ) {
search_box - > grab_focus ( ) ;
}
2021-05-05 03:05:09 +02:00
_start_state_save_timer ( ) ;
2020-08-16 09:30:52 +02:00
}
void EditorLog : : _search_changed ( const String & p_text ) {
_rebuild_log ( ) ;
}
void EditorLog : : _reset_message_counts ( ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < MessageType , LogFilter * > & E : type_filter_map ) {
E . value - > set_message_count ( 0 ) ;
2020-08-16 09:30:52 +02:00
}
2014-02-10 02:10:30 +01:00
}
EditorLog : : EditorLog ( ) {
2021-05-05 03:05:09 +02:00
save_state_timer = memnew ( Timer ) ;
save_state_timer - > set_wait_time ( 2 ) ;
save_state_timer - > set_one_shot ( true ) ;
save_state_timer - > connect ( " timeout " , callable_mp ( this , & EditorLog : : _save_state ) ) ;
add_child ( save_state_timer ) ;
2023-11-20 13:06:54 +01:00
line_limit = int ( EDITOR_GET ( " run/output/max_lines " ) ) ;
EditorSettings : : get_singleton ( ) - > connect ( " settings_changed " , callable_mp ( this , & EditorLog : : _editor_settings_changed ) ) ;
2020-08-16 09:30:52 +02:00
HBoxContainer * hb = this ;
2014-02-10 02:10:30 +01:00
2020-08-16 09:30:52 +02:00
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.
2017-03-05 16:44:50 +01:00
log = memnew ( RichTextLabel ) ;
2023-02-23 10:42:58 +01:00
log - > set_threaded ( true ) ;
2022-05-03 01:29:38 +02:00
log - > set_use_bbcode ( true ) ;
2014-02-10 02:10:30 +01:00
log - > set_scroll_follow ( true ) ;
2014-09-22 05:50:48 +02:00
log - > set_selection_enabled ( true ) ;
2023-02-28 14:19:48 +01:00
log - > set_context_menu_enabled ( true ) ;
2014-09-22 05:50:48 +02:00
log - > set_focus_mode ( FOCUS_CLICK ) ;
2017-09-08 22:39:41 +02:00
log - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
log - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2022-04-04 22:21:49 +02:00
log - > set_deselect_on_focus_loss_enabled ( false ) ;
2024-01-15 14:29:52 +01:00
log - > connect ( " meta_clicked " , callable_mp ( this , & EditorLog : : _meta_clicked ) ) ;
2020-08-16 09:30:52 +02:00
vb_left - > add_child ( log ) ;
// Search box
search_box = memnew ( LineEdit ) ;
search_box - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2022-05-27 06:02:48 +02:00
search_box - > set_placeholder ( TTR ( " Filter Messages " ) ) ;
2020-08-16 09:30:52 +02:00
search_box - > set_clear_button_enabled ( true ) ;
search_box - > set_visible ( true ) ;
search_box - > connect ( " text_changed " , callable_mp ( this , & EditorLog : : _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 ) ;
2023-09-19 18:03:10 +02:00
clear_button - > set_theme_type_variation ( " FlatButton " ) ;
2020-08-16 09:30:52 +02:00
clear_button - > set_focus_mode ( FOCUS_NONE ) ;
2024-03-15 08:40:21 +01:00
clear_button - > set_shortcut ( ED_SHORTCUT ( " editor/clear_output " , TTR ( " Clear Output " ) , KeyModifierMask : : CMD_OR_CTRL | KeyModifierMask : : ALT | Key : : K ) ) ;
2024-05-14 09:40:21 +02:00
clear_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorLog : : _clear_request ) ) ;
2020-08-16 09:30:52 +02:00
hb_tools - > add_child ( clear_button ) ;
// Copy.
copy_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
copy_button - > set_theme_type_variation ( " FlatButton " ) ;
2020-08-16 09:30:52 +02:00
copy_button - > set_focus_mode ( FOCUS_NONE ) ;
2022-09-02 11:37:48 +02:00
copy_button - > set_shortcut ( ED_SHORTCUT ( " editor/copy_output " , TTR ( " Copy Selection " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : C ) ) ;
2021-05-04 16:25:14 +02:00
copy_button - > set_shortcut_context ( this ) ;
2024-05-14 09:40:21 +02:00
copy_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorLog : : _copy_request ) ) ;
2020-08-16 09:30:52 +02:00
hb_tools - > add_child ( copy_button ) ;
2023-09-26 16:41:50 +02:00
// Separate toggle buttons from normal buttons.
vb_right - > add_child ( memnew ( HSeparator ) ) ;
2020-08-16 09:30:52 +02:00
// 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 ) ;
2023-09-19 18:03:10 +02:00
collapse_button - > set_theme_type_variation ( " FlatButton " ) ;
2020-08-16 09:30:52 +02:00
collapse_button - > set_focus_mode ( FOCUS_NONE ) ;
2022-08-25 12:42:17 +02:00
collapse_button - > set_tooltip_text ( TTR ( " Collapse duplicate messages into one log entry. Shows number of occurrences. " ) ) ;
2020-08-16 09:30:52 +02:00
collapse_button - > set_toggle_mode ( true ) ;
collapse_button - > set_pressed ( false ) ;
collapse_button - > connect ( " toggled " , callable_mp ( this , & EditorLog : : _set_collapse ) ) ;
hb_tools2 - > add_child ( collapse_button ) ;
// Show Search.
show_search_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
show_search_button - > set_theme_type_variation ( " FlatButton " ) ;
2020-08-16 09:30:52 +02:00
show_search_button - > set_focus_mode ( FOCUS_NONE ) ;
show_search_button - > set_toggle_mode ( true ) ;
show_search_button - > set_pressed ( true ) ;
2022-09-02 11:37:48 +02:00
show_search_button - > set_shortcut ( ED_SHORTCUT ( " editor/open_search " , TTR ( " Focus Search/Filter Bar " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : F ) ) ;
2021-05-04 16:25:14 +02:00
show_search_button - > set_shortcut_context ( this ) ;
2020-08-16 09:30:52 +02:00
show_search_button - > connect ( " toggled " , callable_mp ( this , & EditorLog : : _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 ) ) ;
2021-07-19 21:10:05 +02:00
std_filter - > initialize_button ( TTR ( " Toggle visibility of standard output messages. " ) , callable_mp ( this , & EditorLog : : _set_filter_active ) ) ;
2020-08-16 09:30:52 +02:00
vb_right - > add_child ( std_filter - > toggle_button ) ;
type_filter_map . insert ( MSG_TYPE_STD , std_filter ) ;
2022-05-03 01:29:38 +02:00
type_filter_map . insert ( MSG_TYPE_STD_RICH , std_filter ) ;
2020-08-16 09:30:52 +02:00
LogFilter * error_filter = memnew ( LogFilter ( MSG_TYPE_ERROR ) ) ;
2021-07-19 21:10:05 +02:00
error_filter - > initialize_button ( TTR ( " Toggle visibility of errors. " ) , callable_mp ( this , & EditorLog : : _set_filter_active ) ) ;
2020-08-16 09:30:52 +02:00
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 ) ) ;
2021-07-19 21:10:05 +02:00
warning_filter - > initialize_button ( TTR ( " Toggle visibility of warnings. " ) , callable_mp ( this , & EditorLog : : _set_filter_active ) ) ;
2020-08-16 09:30:52 +02:00
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 ) ) ;
2021-07-19 21:10:05 +02:00
editor_filter - > initialize_button ( TTR ( " Toggle visibility of editor messages. " ) , callable_mp ( this , & EditorLog : : _set_filter_active ) ) ;
2020-08-16 09:30:52 +02:00
vb_right - > add_child ( editor_filter - > toggle_button ) ;
type_filter_map . insert ( MSG_TYPE_EDITOR , editor_filter ) ;
2023-01-05 13:25:55 +01:00
add_message ( VERSION_FULL_NAME " (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors. " ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
eh . errfunc = _error_handler ;
eh . userdata = this ;
2014-02-10 02:10:30 +01:00
add_error_handler ( & eh ) ;
2017-08-07 12:17:31 +02:00
current = Thread : : get_caller_id ( ) ;
2014-02-10 02:10:30 +01:00
}
void EditorLog : : deinit ( ) {
remove_error_handler ( & eh ) ;
}
EditorLog : : ~ EditorLog ( ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < MessageType , LogFilter * > & E : type_filter_map ) {
2022-05-03 01:29:38 +02:00
// 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 ) ;
}
2020-08-16 09:30:52 +02:00
}
2014-02-10 02:10:30 +01:00
}