Path: blob/master/editor/debugger/script_editor_debugger.cpp
20836 views
/**************************************************************************/1/* script_editor_debugger.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "script_editor_debugger.h"3132#include "core/config/project_settings.h"33#include "core/debugger/debugger_marshalls.h"34#include "core/debugger/remote_debugger.h"35#include "core/string/ustring.h"36#include "core/version.h"37#include "editor/debugger/editor_debugger_plugin.h"38#include "editor/debugger/editor_expression_evaluator.h"39#include "editor/debugger/editor_performance_profiler.h"40#include "editor/debugger/editor_profiler.h"41#include "editor/debugger/editor_visual_profiler.h"42#include "editor/docks/filesystem_dock.h"43#include "editor/docks/inspector_dock.h"44#include "editor/editor_log.h"45#include "editor/editor_node.h"46#include "editor/editor_string_names.h"47#include "editor/file_system/editor_file_system.h"48#include "editor/gui/editor_file_dialog.h"49#include "editor/gui/editor_toaster.h"50#include "editor/inspector/editor_property_name_processor.h"51#include "editor/scene/3d/node_3d_editor_plugin.h"52#include "editor/scene/canvas_item_editor_plugin.h"53#include "editor/settings/editor_settings.h"54#include "editor/themes/editor_scale.h"55#include "main/performance.h"56#include "scene/3d/camera_3d.h"57#include "scene/debugger/scene_debugger_object.h"58#include "scene/gui/button.h"59#include "scene/gui/dialogs.h"60#include "scene/gui/grid_container.h"61#include "scene/gui/label.h"62#include "scene/gui/line_edit.h"63#include "scene/gui/margin_container.h"64#include "scene/gui/separator.h"65#include "scene/gui/split_container.h"66#include "scene/gui/tab_container.h"67#include "scene/gui/tree.h"68#include "servers/debugger/servers_debugger.h"69#include "servers/display/display_server.h"7071using CameraOverride = EditorDebuggerNode::CameraOverride;7273void ScriptEditorDebugger::_put_msg(const String &p_message, const Array &p_data, uint64_t p_thread_id) {74ERR_FAIL_COND(p_thread_id == Thread::UNASSIGNED_ID);75if (is_session_active()) {76Array msg = { p_message, p_thread_id, p_data };77Error err = peer->put_message(msg);78ERR_FAIL_COND_MSG(err != OK, vformat("Failed to send message %d", err));79}80}8182void ScriptEditorDebugger::debug_copy() {83String msg = reason->get_text();84if (msg.is_empty()) {85return;86}87DisplayServer::get_singleton()->clipboard_set(msg);88}8990void ScriptEditorDebugger::debug_skip_breakpoints() {91skip_breakpoints_value = !skip_breakpoints_value;92if (skip_breakpoints_value) {93skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));94} else {95skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));96}9798Array msg = { skip_breakpoints_value };99_put_msg("set_skip_breakpoints", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);100}101102void ScriptEditorDebugger::debug_ignore_error_breaks() {103ignore_error_breaks_value = !ignore_error_breaks_value;104if (ignore_error_breaks_value) {105ignore_error_breaks->set_button_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));106} else {107ignore_error_breaks->set_button_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));108}109110Array msg = { ignore_error_breaks_value };111_put_msg("set_ignore_error_breaks", msg);112}113114void ScriptEditorDebugger::debug_out() {115ERR_FAIL_COND(!is_breaked());116117_put_msg("out", Array(), debugging_thread_id);118_clear_execution();119}120121void ScriptEditorDebugger::debug_next() {122ERR_FAIL_COND(!is_breaked());123124_put_msg("next", Array(), debugging_thread_id);125_clear_execution();126}127128void ScriptEditorDebugger::debug_step() {129ERR_FAIL_COND(!is_breaked());130131_put_msg("step", Array(), debugging_thread_id);132_clear_execution();133}134135void ScriptEditorDebugger::debug_break() {136ERR_FAIL_COND(is_breaked());137138_put_msg("break", Array());139_mute_audio_on_break(true);140}141142void ScriptEditorDebugger::debug_continue() {143ERR_FAIL_COND(!is_breaked());144145// Allow focus stealing only if we actually run this client for security.146if (remote_pid && EditorNode::get_singleton()->has_child_process(remote_pid)) {147DisplayServer::get_singleton()->enable_for_stealing_focus(remote_pid);148}149150_clear_execution();151_put_msg("continue", Array(), debugging_thread_id);152_put_msg("servers:foreground", Array());153_mute_audio_on_break(false);154}155156void ScriptEditorDebugger::update_tabs() {157if (error_count == 0 && warning_count == 0) {158errors_tab->set_name(TTRC("Errors"));159tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), Ref<Texture2D>());160} else {161errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")");162if (error_count >= 1 && warning_count >= 1) {163tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("ErrorWarning")));164} else if (error_count >= 1) {165tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("Error")));166} else {167tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("Warning")));168}169}170}171172void ScriptEditorDebugger::clear_style() {173tabs->remove_theme_style_override(SceneStringName(panel));174}175176void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) {177Array msg = { p_id, p_file };178_put_msg("scene:save_node", msg);179}180181void ScriptEditorDebugger::_file_selected(const String &p_file) {182switch (file_dialog_purpose) {183case SAVE_MONITORS_CSV: {184Error err;185Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);186187if (err != OK) {188ERR_PRINT("Failed to open " + p_file);189return;190}191Vector<String> line;192line.resize(Performance::MONITOR_MAX);193194// signatures195for (int i = 0; i < Performance::MONITOR_MAX; i++) {196line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i));197}198file->store_csv_line(line);199200// values201Vector<List<float>::Element *> iterators;202iterators.resize(Performance::MONITOR_MAX);203bool continue_iteration = false;204for (int i = 0; i < Performance::MONITOR_MAX; i++) {205iterators.write[i] = performance_profiler->get_monitor_data(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)))->back();206continue_iteration = continue_iteration || iterators[i];207}208while (continue_iteration) {209continue_iteration = false;210for (int i = 0; i < Performance::MONITOR_MAX; i++) {211if (iterators[i]) {212line.write[i] = String::num_real(iterators[i]->get());213iterators.write[i] = iterators[i]->prev();214} else {215line.write[i] = "";216}217continue_iteration = continue_iteration || iterators[i];218}219file->store_csv_line(line);220}221file->store_string("\n");222223Vector<Vector<String>> profiler_data = profiler->get_data_as_csv();224for (int i = 0; i < profiler_data.size(); i++) {225file->store_csv_line(profiler_data[i]);226}227} break;228case SAVE_VRAM_CSV: {229Error err;230Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);231232if (err != OK) {233ERR_PRINT("Failed to open " + p_file);234return;235}236237Vector<String> headers;238headers.resize(vmem_tree->get_columns());239for (int i = 0; i < vmem_tree->get_columns(); ++i) {240headers.write[i] = vmem_tree->get_column_title(i);241}242file->store_csv_line(headers);243244if (vmem_tree->get_root()) {245TreeItem *ti = vmem_tree->get_root()->get_first_child();246while (ti) {247Vector<String> values;248values.resize(vmem_tree->get_columns());249for (int i = 0; i < vmem_tree->get_columns(); ++i) {250values.write[i] = ti->get_text(i);251}252file->store_csv_line(values);253254ti = ti->get_next();255}256}257} break;258}259}260261void ScriptEditorDebugger::request_remote_tree() {262_put_msg("scene:request_scene_tree", Array());263}264265const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {266return scene_tree;267}268269void ScriptEditorDebugger::request_remote_evaluate(const String &p_expression, int p_stack_frame) {270Array msg = { p_expression, p_stack_frame };271_put_msg("evaluate", msg);272}273274void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value, const String &p_field) {275Array msg = { p_obj_id, p_prop };276277Ref<Resource> res = p_value;278if (res.is_valid() && !res->get_path().is_empty()) {279msg.append(res->get_path());280} else {281msg.append(p_value);282}283284if (p_field.is_empty()) {285_put_msg("scene:set_object_property", msg);286} else {287msg.push_back(p_field);288_put_msg("scene:set_object_property_field", msg);289}290}291292void ScriptEditorDebugger::request_remote_objects(const TypedArray<uint64_t> &p_obj_ids, bool p_update_selection) {293ERR_FAIL_COND(p_obj_ids.is_empty());294Array msg = { p_obj_ids.duplicate(), p_update_selection };295_put_msg("scene:inspect_objects", msg);296}297298void ScriptEditorDebugger::clear_inspector(bool p_send_msg) {299inspector->clear_remote_inspector();300if (p_send_msg) {301_put_msg("scene:clear_selection", Array());302}303}304305void ScriptEditorDebugger::_remote_object_selected(ObjectID p_id) {306emit_signal(SNAME("remote_objects_requested"), Array{ p_id });307}308309void ScriptEditorDebugger::_remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field) {310for (const KeyValue<Variant, Variant> &kv : p_values) {311update_remote_object(ObjectID(static_cast<uint64_t>(kv.key)), p_prop, kv.value, p_field);312}313request_remote_objects(p_values.keys(), false);314}315316void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const String &p_property) {317emit_signal(SNAME("remote_object_property_updated"), p_id, p_property);318}319320void ScriptEditorDebugger::_video_mem_request() {321_put_msg("servers:memory", Array());322}323324void ScriptEditorDebugger::_video_mem_export() {325file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);326file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);327file_dialog->clear_filters();328file_dialog_purpose = SAVE_VRAM_CSV;329file_dialog->popup_file_dialog();330}331332Size2 ScriptEditorDebugger::get_minimum_size() const {333Size2 ms = MarginContainer::get_minimum_size();334ms.y = MAX(ms.y, 250 * EDSCALE);335return ms;336}337338void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) {339ERR_FAIL_COND(!threads_debugged.has(p_thread_id));340ThreadDebugged &td = threads_debugged[p_thread_id];341_set_reason_text(td.error, MESSAGE_ERROR);342emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump);343_mute_audio_on_break(true);344if (!td.error.is_empty() && EDITOR_GET("debugger/auto_switch_to_stack_trace")) {345tabs->set_current_tab(0);346}347inspector->clear_cache(); // Take a chance to force remote objects update.348_put_msg("get_stack_dump", Array(), p_thread_id);349}350351void ScriptEditorDebugger::_select_thread(int p_index) {352debugging_thread_id = threads->get_item_metadata(threads->get_selected());353_thread_debug_enter(debugging_thread_id);354}355356void ScriptEditorDebugger::_msg_debug_enter(uint64_t p_thread_id, const Array &p_data) {357ERR_FAIL_COND(p_data.size() != 4);358359const Thread::ID caller_id = p_data[3];360361ThreadDebugged td;362td.name = (caller_id == Thread::get_main_id()) ? TTR("Main Thread") : itos(caller_id);363td.error = p_data[1];364td.can_debug = p_data[0];365td.has_stackdump = p_data[2];366td.thread_id = p_thread_id;367static uint32_t order_inc = 0;368td.debug_order = order_inc++;369370threads_debugged.insert(p_thread_id, td);371372if (threads_debugged.size() == 1) {373// First thread that requests debug374debugging_thread_id = p_thread_id;375_thread_debug_enter(p_thread_id);376can_request_idle_draw = true;377if (is_move_to_foreground()) {378DisplayServer::get_singleton()->window_move_to_foreground();379}380profiler->set_enabled(false, false);381visual_profiler->set_enabled(false);382}383_update_buttons_state();384}385386void ScriptEditorDebugger::_msg_debug_exit(uint64_t p_thread_id, const Array &p_data) {387threads_debugged.erase(p_thread_id);388if (p_thread_id == debugging_thread_id) {389_clear_execution();390if (threads_debugged.is_empty()) {391debugging_thread_id = Thread::UNASSIGNED_ID;392} else {393// Find next thread to debug.394uint32_t min_order = 0xFFFFFFFF;395uint64_t next_thread = Thread::UNASSIGNED_ID;396for (KeyValue<uint64_t, ThreadDebugged> T : threads_debugged) {397if (T.value.debug_order < min_order) {398min_order = T.value.debug_order;399next_thread = T.key;400}401}402403debugging_thread_id = next_thread;404}405406if (debugging_thread_id == Thread::UNASSIGNED_ID) {407// Nothing else to debug.408profiler->set_enabled(true, false);409profiler->disable_seeking();410411visual_profiler->set_enabled(true);412413_set_reason_text(TTRC("Execution resumed."), MESSAGE_SUCCESS);414emit_signal(SNAME("breaked"), false, false, "", false);415_mute_audio_on_break(false);416417_update_buttons_state();418} else {419_thread_debug_enter(debugging_thread_id);420}421} else {422_update_buttons_state();423}424}425426void ScriptEditorDebugger::_msg_set_pid(uint64_t p_thread_id, const Array &p_data) {427ERR_FAIL_COND(p_data.is_empty());428remote_pid = p_data[0];429// We emit the started signal after we've set the PID.430emit_signal(SNAME("started"));431}432433void ScriptEditorDebugger::_msg_scene_click_ctrl(uint64_t p_thread_id, const Array &p_data) {434ERR_FAIL_COND(p_data.size() < 2);435clicked_ctrl->set_text(p_data[0]);436clicked_ctrl_type->set_text(p_data[1]);437}438439void ScriptEditorDebugger::_msg_scene_scene_tree(uint64_t p_thread_id, const Array &p_data) {440scene_tree->nodes.clear();441scene_tree->deserialize(p_data);442emit_signal(SNAME("remote_tree_updated"));443_update_buttons_state();444}445446void ScriptEditorDebugger::_msg_scene_inspect_objects(uint64_t p_thread_id, const Array &p_data) {447ERR_FAIL_COND(p_data.is_empty());448EditorDebuggerRemoteObjects *objs = inspector->set_objects(p_data);449if (objs && EditorDebuggerNode::get_singleton()->match_remote_selection(objs->remote_object_ids)) {450EditorDebuggerNode::get_singleton()->stop_waiting_inspection();451452emit_signal(SNAME("remote_objects_updated"), objs);453}454}455456#ifndef DISABLE_DEPRECATED457void ScriptEditorDebugger::_msg_scene_inspect_object(uint64_t p_thread_id, const Array &p_data) {458ERR_FAIL_COND(p_data.is_empty());459// Legacy compatibility: convert single object response to new format.460// p_data is [id, className, properties] - wrap it as first element of array for new handler.461Array wrapped_data;462wrapped_data.push_back(p_data);463_msg_scene_inspect_objects(p_thread_id, wrapped_data);464}465#endif // DISABLE_DEPRECATED466467void ScriptEditorDebugger::_msg_scene_debug_mute_audio(uint64_t p_thread_id, const Array &p_data) {468ERR_FAIL_COND(p_data.is_empty());469// This is handled by SceneDebugger, we need to ignore here to not show a warning.470}471472void ScriptEditorDebugger::_msg_servers_memory_usage(uint64_t p_thread_id, const Array &p_data) {473vmem_tree->clear();474TreeItem *root = vmem_tree->create_item();475ServersDebugger::ResourceUsage usage;476usage.deserialize(p_data);477478uint64_t total = 0;479480for (const ServersDebugger::ResourceInfo &E : usage.infos) {481TreeItem *it = vmem_tree->create_item(root);482String type = E.type;483int bytes = E.vram;484it->set_text(0, E.path);485it->set_text(1, type);486it->set_text(2, E.format);487it->set_text(3, String::humanize_size(bytes));488total += bytes;489490// If it does not have a theme icon, just go up the inheritance tree until we find one.491if (!has_theme_icon(type, EditorStringName(EditorIcons))) {492StringName base_type = type;493while (base_type != "Resource" || base_type != "") {494base_type = ClassDB::get_parent_class(base_type);495if (has_theme_icon(base_type, EditorStringName(EditorIcons))) {496type = base_type;497break;498}499}500}501502it->set_icon(0, get_editor_theme_icon(type));503}504505vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total));506vmem_total->set_text(String::humanize_size(total));507}508509void ScriptEditorDebugger::_msg_servers_drawn(uint64_t p_thread_id, const Array &p_data) {510can_request_idle_draw = true;511}512513void ScriptEditorDebugger::_msg_stack_dump(uint64_t p_thread_id, const Array &p_data) {514DebuggerMarshalls::ScriptStackDump stack;515stack.deserialize(p_data);516517stack_dump->clear();518inspector->clear_stack_variables();519TreeItem *r = stack_dump->create_item();520521Array stack_dump_info;522523int i = 0;524for (List<ScriptLanguage::StackInfo>::Iterator itr = stack.frames.begin(); itr != stack.frames.end(); ++itr, ++i) {525TreeItem *s = stack_dump->create_item(r);526Dictionary d;527d["frame"] = i;528d["file"] = itr->file;529d["function"] = itr->func;530d["line"] = itr->line;531stack_dump_info.push_back(d);532s->set_metadata(0, d);533534String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + String(d["function"]);535s->set_text(0, line);536537if (i == 0) {538s->select(0);539}540}541emit_signal(SNAME("stack_dump"), stack_dump_info);542}543544void ScriptEditorDebugger::_msg_stack_frame_vars(uint64_t p_thread_id, const Array &p_data) {545inspector->clear_stack_variables();546ERR_FAIL_COND(p_data.size() != 1);547emit_signal(SNAME("stack_frame_vars"), p_data[0]);548}549550void ScriptEditorDebugger::_msg_stack_frame_var(uint64_t p_thread_id, const Array &p_data) {551inspector->add_stack_variable(p_data);552emit_signal(SNAME("stack_frame_var"), p_data);553}554555void ScriptEditorDebugger::_msg_output(uint64_t p_thread_id, const Array &p_data) {556ERR_FAIL_COND(p_data.size() != 2);557558ERR_FAIL_COND(p_data[0].get_type() != Variant::PACKED_STRING_ARRAY);559Vector<String> output_strings = p_data[0];560561ERR_FAIL_COND(p_data[1].get_type() != Variant::PACKED_INT32_ARRAY);562Vector<int> output_types = p_data[1];563564ERR_FAIL_COND(output_strings.size() != output_types.size());565566for (int i = 0; i < output_strings.size(); i++) {567RemoteDebugger::MessageType type = (RemoteDebugger::MessageType)(int)(output_types[i]);568EditorLog::MessageType msg_type;569switch (type) {570case RemoteDebugger::MESSAGE_TYPE_LOG: {571msg_type = EditorLog::MSG_TYPE_STD;572} break;573case RemoteDebugger::MESSAGE_TYPE_LOG_RICH: {574msg_type = EditorLog::MSG_TYPE_STD_RICH;575} break;576case RemoteDebugger::MESSAGE_TYPE_ERROR: {577msg_type = EditorLog::MSG_TYPE_ERROR;578} break;579default: {580WARN_PRINT("Unhandled script debugger message type: " + itos(type));581msg_type = EditorLog::MSG_TYPE_STD;582} break;583}584EditorNode::get_log()->add_message(output_strings[i], msg_type);585emit_signal(SceneStringName(output), output_strings[i], msg_type);586}587}588589void ScriptEditorDebugger::_msg_performance_profile_frame(uint64_t p_thread_id, const Array &p_data) {590Vector<float> frame_data;591frame_data.resize(p_data.size());592for (int i = 0; i < p_data.size(); i++) {593frame_data.write[i] = p_data[i];594}595performance_profiler->add_profile_frame(frame_data);596}597598void ScriptEditorDebugger::_msg_visual_hardware_info(uint64_t p_thread_id, const Array &p_data) {599const String cpu_name = p_data[0];600const String gpu_name = p_data[1];601visual_profiler->set_hardware_info(cpu_name, gpu_name);602}603604void ScriptEditorDebugger::_msg_visual_profile_frame(uint64_t p_thread_id, const Array &p_data) {605ServersDebugger::VisualProfilerFrame frame;606frame.deserialize(p_data);607608EditorVisualProfiler::Metric metric;609metric.areas.resize(frame.areas.size());610metric.frame_number = frame.frame_number;611metric.valid = true;612613{614EditorVisualProfiler::Metric::Area *areas_ptr = metric.areas.ptrw();615for (int i = 0; i < frame.areas.size(); i++) {616areas_ptr[i].name = frame.areas[i].name;617areas_ptr[i].cpu_time = frame.areas[i].cpu_msec;618areas_ptr[i].gpu_time = frame.areas[i].gpu_msec;619}620}621visual_profiler->add_frame_metric(metric);622}623624void ScriptEditorDebugger::_msg_error(uint64_t p_thread_id, const Array &p_data) {625DebuggerMarshalls::OutputError oe;626ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");627628// Format time.629Array time_vals = { oe.hr, oe.min, oe.sec, oe.msec };630bool e;631String time = String("%d:%02d:%02d:%03d").sprintf(time_vals, &e);632633// Rest of the error data.634bool source_is_project_file = oe.source_file.begins_with("res://");635636// Metadata to highlight error line in scripts.637Array source_meta = { oe.source_file, oe.source_line };638639// Create error tree to display above error or warning details.640TreeItem *r = error_tree->get_root();641if (!r) {642r = error_tree->create_item();643}644645// Also provide the relevant details as tooltip to quickly check without646// uncollapsing the tree.647String tooltip = oe.warning ? TTRC("Warning:") : TTRC("Error:");648649TreeItem *error = error_tree->create_item(r);650if (oe.warning) {651error->set_meta("_is_warning", true);652} else {653error->set_meta("_is_error", true);654}655error->set_collapsed(true);656657error->set_icon(0, get_editor_theme_icon(oe.warning ? SNAME("Warning") : SNAME("Error")));658error->set_text(0, time);659error->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);660661const Color color = get_theme_color(oe.warning ? SNAME("warning_color") : SNAME("error_color"), EditorStringName(Editor));662error->set_custom_color(0, color);663error->set_custom_color(1, color);664665String error_title;666if (!oe.source_func.is_empty() && source_is_project_file) {667// If source function is inside the project file.668error_title += oe.source_func + ": ";669} else if (oe.callstack.size() > 0) {670// Otherwise, if available, use the script's stack in the error title.671error_title = _format_frame_text(&oe.callstack[0]) + ": ";672} else if (!oe.source_func.is_empty()) {673// Otherwise try to use the C++ source function.674error_title += oe.source_func + ": ";675}676// If we have a (custom) error message, use it as title, and add a C++ Error677// item with the original error condition.678error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr;679error->set_text(1, error_title);680error->set_autowrap_mode(1, TextServer::AUTOWRAP_WORD_SMART);681error->set_autowrap_trim_flags(1, 0);682tooltip += " " + error_title + "\n";683684// Find the language of the error's source file.685String source_language_name = "C++"; // Default value is the old hard-coded one.686const String source_file_extension = oe.source_file.get_extension();687for (int i = 0; i < ScriptServer::get_language_count(); ++i) {688ScriptLanguage *script_language = ScriptServer::get_language(i);689if (source_file_extension == script_language->get_extension()) {690source_language_name = script_language->get_name();691break;692}693}694695if (!oe.error_descr.is_empty()) {696// Add item for C++ error condition.697TreeItem *cpp_cond = error_tree->create_item(error);698// TRANSLATORS: %s is the name of a language, e.g. C++.699cpp_cond->set_text(0, "<" + vformat(TTR("%s Error"), source_language_name) + ">");700cpp_cond->set_text(1, oe.error);701cpp_cond->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);702tooltip += vformat(TTR("%s Error:"), source_language_name) + " " + oe.error + "\n";703if (source_is_project_file) {704cpp_cond->set_metadata(0, source_meta);705}706}707Vector<uint8_t> v;708v.resize(100);709710// Source of the error.711String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line);712if (!oe.source_func.is_empty()) {713source_txt += " @ " + oe.source_func;714if (!oe.source_func.ends_with(")")) {715source_txt += "()";716}717}718719TreeItem *cpp_source = error_tree->create_item(error);720// TRANSLATORS: %s is the name of a language, e.g. C++.721cpp_source->set_text(0, "<" + vformat(TTR("%s Source"), source_language_name) + ">");722cpp_source->set_text(1, source_txt);723cpp_source->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);724tooltip += vformat(TTR("%s Source:"), source_language_name) + " " + source_txt + "\n";725726// Set metadata to highlight error line in scripts.727if (source_is_project_file) {728error->set_metadata(0, source_meta);729cpp_source->set_metadata(0, source_meta);730}731732// Format stack trace.733// stack_items_count is the number of elements to parse, with 3 items per frame734// of the stack trace (script, method, line).735const ScriptLanguage::StackInfo *infos = oe.callstack.ptr();736for (unsigned int i = 0; i < (unsigned int)oe.callstack.size(); i++) {737TreeItem *stack_trace = error_tree->create_item(error);738739Array meta = { infos[i].file, infos[i].line };740stack_trace->set_metadata(0, meta);741742if (i == 0) {743stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">");744stack_trace->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);745if (!source_is_project_file) {746// Only override metadata if the source is not inside the project.747error->set_metadata(0, meta);748}749tooltip += TTR("Stack Trace:") + "\n";750}751752String frame_txt = _format_frame_text(&infos[i]);753tooltip += frame_txt + "\n";754stack_trace->set_text(1, frame_txt);755}756757error->set_tooltip_text(0, tooltip);758error->set_tooltip_text(1, tooltip);759760if (warning_count == 0 && error_count == 0) {761expand_all_button->set_disabled(false);762collapse_all_button->set_disabled(false);763clear_button->set_disabled(false);764}765766if (oe.warning) {767warning_count++;768} else {769error_count++;770}771}772773void ScriptEditorDebugger::_msg_servers_function_signature(uint64_t p_thread_id, const Array &p_data) {774// Cache a profiler signature.775ServersDebugger::ScriptFunctionSignature sig;776sig.deserialize(p_data);777profiler_signature[sig.id] = sig.name;778}779780void ScriptEditorDebugger::_msg_servers_profile_common(const Array &p_data, const bool p_final) {781EditorProfiler::Metric metric;782ServersDebugger::ServersProfilerFrame frame;783frame.deserialize(p_data);784metric.valid = true;785metric.frame_number = frame.frame_number;786metric.frame_time = frame.frame_time;787metric.process_time = frame.process_time;788metric.physics_time = frame.physics_time;789metric.physics_frame_time = frame.physics_frame_time;790791if (frame.servers.size()) {792EditorProfiler::Metric::Category frame_time;793frame_time.signature = "category_frame_time";794frame_time.name = "Frame Time";795frame_time.total_time = metric.frame_time;796797EditorProfiler::Metric::Category::Item item;798item.calls = 1;799item.line = 0;800801item.name = "Physics Time";802item.total = metric.physics_time;803item.self = item.total;804item.signature = "physics_time";805806frame_time.items.push_back(item);807808item.name = "Process Time";809item.total = metric.process_time;810item.self = item.total;811item.signature = "process_time";812813frame_time.items.push_back(item);814815item.name = "Physics Frame Time";816item.total = metric.physics_frame_time;817item.self = item.total;818item.signature = "physics_frame_time";819820frame_time.items.push_back(item);821822metric.categories.push_back(frame_time);823}824825for (const ServersDebugger::ServerInfo &srv : frame.servers) {826EditorProfiler::Metric::Category c;827const String name = srv.name;828c.name = EditorPropertyNameProcessor::get_singleton()->process_name(name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);829c.items.resize(srv.functions.size());830c.total_time = 0;831c.signature = "categ::" + name;832int j = 0;833for (List<ServersDebugger::ServerFunctionInfo>::ConstIterator itr = srv.functions.begin(); itr != srv.functions.end(); ++itr, ++j) {834EditorProfiler::Metric::Category::Item item;835item.calls = 1;836item.line = 0;837item.name = itr->name;838item.self = itr->time;839item.total = item.self;840item.signature = "categ::" + name + "::" + item.name;841item.name = EditorPropertyNameProcessor::get_singleton()->process_name(item.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);842c.total_time += item.total;843c.items.write[j] = item;844}845metric.categories.push_back(c);846}847848EditorProfiler::Metric::Category funcs;849funcs.total_time = frame.script_time;850funcs.items.resize(frame.script_functions.size());851funcs.name = "Script Functions";852funcs.signature = "script_functions";853for (int i = 0; i < frame.script_functions.size(); i++) {854int signature = frame.script_functions[i].sig_id;855int calls = frame.script_functions[i].call_count;856float total = frame.script_functions[i].total_time;857float self = frame.script_functions[i].self_time;858float internal = frame.script_functions[i].internal_time;859860EditorProfiler::Metric::Category::Item item;861if (profiler_signature.has(signature)) {862item.signature = profiler_signature[signature];863864String name = profiler_signature[signature];865Vector<String> strings = name.split("::");866if (strings.size() == 3) {867item.name = strings[2];868item.script = strings[0];869item.line = strings[1].to_int();870} else if (strings.size() == 4) { //Built-in scripts have an :: in their name871item.name = strings[3];872item.script = strings[0] + "::" + strings[1];873item.line = strings[2].to_int();874}875876} else {877item.name = "SigErr " + itos(signature);878}879880item.calls = calls;881item.self = self;882item.total = total;883item.internal = internal;884funcs.items.write[i] = item;885}886887metric.categories.push_back(funcs);888889profiler->add_frame_metric(metric, p_final);890}891892void ScriptEditorDebugger::_msg_servers_profile_frame(uint64_t p_thread_id, const Array &p_data) {893_msg_servers_profile_common(p_data, false);894}895void ScriptEditorDebugger::_msg_servers_profile_total(uint64_t p_thread_id, const Array &p_data) {896_msg_servers_profile_common(p_data, true);897}898899void ScriptEditorDebugger::_msg_request_quit(uint64_t p_thread_id, const Array &p_data) {900emit_signal(SNAME("stop_requested"));901_stop_and_notify();902}903904void ScriptEditorDebugger::_msg_remote_objects_selected(uint64_t p_thread_id, const Array &p_data) {905ERR_FAIL_COND(p_data.is_empty());906EditorDebuggerRemoteObjects *objs = inspector->set_objects(p_data);907if (objs) {908EditorDebuggerNode::get_singleton()->stop_waiting_inspection();909910emit_signal(SNAME("remote_objects_updated"), objs);911emit_signal(SNAME("remote_tree_select_requested"), objs->remote_object_ids.duplicate());912}913}914915void ScriptEditorDebugger::_msg_remote_nothing_selected(uint64_t p_thread_id, const Array &p_data) {916EditorDebuggerNode::get_singleton()->stop_waiting_inspection();917918emit_signal(SNAME("remote_tree_clear_selection_requested"));919}920921void ScriptEditorDebugger::_msg_remote_selection_invalidated(uint64_t p_thread_id, const Array &p_data) {922ERR_FAIL_COND(p_data.is_empty());923inspector->invalidate_selection_from_cache(p_data[0]);924}925926void ScriptEditorDebugger::_msg_show_selection_limit_warning(uint64_t p_thread_id, const Array &p_data) {927EditorToaster::get_singleton()->popup_str(vformat(TTR("Some remote nodes were not selected, as the configured maximum selection is %d. This can be changed at \"debugger/max_node_selection\" in the Editor Settings."), EDITOR_GET("debugger/max_node_selection")), EditorToaster::SEVERITY_WARNING);928}929930void ScriptEditorDebugger::_msg_performance_profile_names(uint64_t p_thread_id, const Array &p_data) {931ERR_FAIL_COND(p_data.size() != 2);932Array name_data = p_data[0];933Array type_data = p_data[1];934935Vector<StringName> monitors;936monitors.resize(name_data.size());937for (int i = 0; i < name_data.size(); i++) {938ERR_FAIL_COND(name_data[i].get_type() != Variant::STRING_NAME);939monitors.set(i, name_data[i]);940}941942PackedInt32Array types;943types.resize(type_data.size());944for (int i = 0; i < type_data.size(); i++) {945ERR_FAIL_COND(type_data[i].get_type() != Variant::INT);946types.set(i, type_data[i]);947}948949performance_profiler->update_monitors(monitors, types);950}951952void ScriptEditorDebugger::_msg_filesystem_update_file(uint64_t p_thread_id, const Array &p_data) {953ERR_FAIL_COND(p_data.is_empty());954if (EditorFileSystem::get_singleton()) {955EditorFileSystem::get_singleton()->update_file(p_data[0]);956}957}958959void ScriptEditorDebugger::_msg_evaluation_return(uint64_t p_thread_id, const Array &p_data) {960expression_evaluator->add_value(p_data);961}962963void ScriptEditorDebugger::_msg_window_title(uint64_t p_thread_id, const Array &p_data) {964ERR_FAIL_COND(p_data.size() != 1);965emit_signal(SNAME("remote_window_title_changed"), p_data[0]);966}967968void ScriptEditorDebugger::_msg_embed_suspend_toggle(uint64_t p_thread_id, const Array &p_data) {969emit_signal(SNAME("embed_shortcut_requested"), EMBED_SUSPEND_TOGGLE);970}971972void ScriptEditorDebugger::_msg_embed_next_frame(uint64_t p_thread_id, const Array &p_data) {973emit_signal(SNAME("embed_shortcut_requested"), EMBED_NEXT_FRAME);974}975976void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data) {977emit_signal(SNAME("debug_data"), p_msg, p_data);978979ParseMessageFunc *fn_ptr = parse_message_handlers.getptr(p_msg);980if (fn_ptr) {981(this->**fn_ptr)(p_thread_id, p_data);982} else {983int colon_index = p_msg.find_char(':');984ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");985986bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);987if (!parsed) {988WARN_PRINT("Unknown message: " + p_msg);989}990}991}992993HashMap<String, ScriptEditorDebugger::ParseMessageFunc> ScriptEditorDebugger::parse_message_handlers;994995void ScriptEditorDebugger::_init_parse_message_handlers() {996parse_message_handlers["debug_enter"] = &ScriptEditorDebugger::_msg_debug_enter;997parse_message_handlers["debug_exit"] = &ScriptEditorDebugger::_msg_debug_exit;998parse_message_handlers["set_pid"] = &ScriptEditorDebugger::_msg_set_pid;999parse_message_handlers["scene:click_ctrl"] = &ScriptEditorDebugger::_msg_scene_click_ctrl;1000parse_message_handlers["scene:scene_tree"] = &ScriptEditorDebugger::_msg_scene_scene_tree;1001parse_message_handlers["scene:inspect_objects"] = &ScriptEditorDebugger::_msg_scene_inspect_objects;1002#ifndef DISABLE_DEPRECATED1003parse_message_handlers["scene:inspect_object"] = &ScriptEditorDebugger::_msg_scene_inspect_object;1004#endif // DISABLE_DEPRECATED1005parse_message_handlers["scene:debug_mute_audio"] = &ScriptEditorDebugger::_msg_scene_debug_mute_audio;1006parse_message_handlers["servers:memory_usage"] = &ScriptEditorDebugger::_msg_servers_memory_usage;1007parse_message_handlers["servers:drawn"] = &ScriptEditorDebugger::_msg_servers_drawn;1008parse_message_handlers["stack_dump"] = &ScriptEditorDebugger::_msg_stack_dump;1009parse_message_handlers["stack_frame_vars"] = &ScriptEditorDebugger::_msg_stack_frame_vars;1010parse_message_handlers["stack_frame_var"] = &ScriptEditorDebugger::_msg_stack_frame_var;1011parse_message_handlers["output"] = &ScriptEditorDebugger::_msg_output;1012parse_message_handlers["performance:profile_frame"] = &ScriptEditorDebugger::_msg_performance_profile_frame;1013parse_message_handlers["visual:hardware_info"] = &ScriptEditorDebugger::_msg_visual_hardware_info;1014parse_message_handlers["visual:profile_frame"] = &ScriptEditorDebugger::_msg_visual_profile_frame;1015parse_message_handlers["error"] = &ScriptEditorDebugger::_msg_error;1016parse_message_handlers["servers:function_signature"] = &ScriptEditorDebugger::_msg_servers_function_signature;1017parse_message_handlers["servers:profile_frame"] = &ScriptEditorDebugger::_msg_servers_profile_frame;1018parse_message_handlers["servers:profile_total"] = &ScriptEditorDebugger::_msg_servers_profile_total;1019parse_message_handlers["request_quit"] = &ScriptEditorDebugger::_msg_request_quit;1020parse_message_handlers["remote_objects_selected"] = &ScriptEditorDebugger::_msg_remote_objects_selected;1021parse_message_handlers["remote_nothing_selected"] = &ScriptEditorDebugger::_msg_remote_nothing_selected;1022parse_message_handlers["remote_selection_invalidated"] = &ScriptEditorDebugger::_msg_remote_selection_invalidated;1023parse_message_handlers["show_selection_limit_warning"] = &ScriptEditorDebugger::_msg_show_selection_limit_warning;1024parse_message_handlers["performance:profile_names"] = &ScriptEditorDebugger::_msg_performance_profile_names;1025parse_message_handlers["filesystem:update_file"] = &ScriptEditorDebugger::_msg_filesystem_update_file;1026parse_message_handlers["evaluation_return"] = &ScriptEditorDebugger::_msg_evaluation_return;1027parse_message_handlers["window:title"] = &ScriptEditorDebugger::_msg_window_title;1028parse_message_handlers["request_embed_suspend_toggle"] = &ScriptEditorDebugger::_msg_embed_suspend_toggle;1029parse_message_handlers["request_embed_next_frame"] = &ScriptEditorDebugger::_msg_embed_next_frame;1030}10311032void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType p_type) {1033switch (p_type) {1034case MESSAGE_ERROR:1035reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1036break;1037case MESSAGE_WARNING:1038reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));1039break;1040default:1041reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("success_color"), EditorStringName(Editor)));1042break;1043}10441045reason->set_text(p_reason);10461047_update_reason_content_height();10481049const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80);1050PackedStringArray lines;1051for (int i = 0; i < boundaries.size(); i += 2) {1052const int start = boundaries[i];1053const int end = boundaries[i + 1];1054lines.append(p_reason.substr(start, end - start));1055}10561057reason->set_tooltip_text(String("\n").join(lines));1058}10591060void ScriptEditorDebugger::_update_reason_content_height() {1061float margin_height = 0;1062const Ref<StyleBox> style = reason->get_theme_stylebox(CoreStringName(normal));1063if (style.is_valid()) {1064margin_height += style->get_content_margin(SIDE_TOP) + style->get_content_margin(SIDE_BOTTOM);1065}10661067const float content_height = margin_height + reason->get_content_height();10681069float content_max_height = margin_height;1070for (int i = 0; i < 3; i++) {1071if (i >= reason->get_line_count()) {1072break;1073}1074content_max_height += reason->get_line_height(i);1075}10761077reason->set_custom_minimum_size(Size2(0, CLAMP(content_height, 0, content_max_height)));1078}10791080void ScriptEditorDebugger::_notification(int p_what) {1081switch (p_what) {1082case NOTIFICATION_POSTINITIALIZE: {1083connect("started", callable_mp(expression_evaluator, &EditorExpressionEvaluator::on_start));1084} break;10851086case NOTIFICATION_TRANSLATION_CHANGED: {1087if (is_ready()) {1088for (TreeItem *file_item = breakpoints_tree->get_root()->get_first_child(); file_item; file_item = file_item->get_next()) {1089for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {1090int line = breakpoint_item->get_meta("line");1091breakpoint_item->set_text(0, vformat(TTR("Line %d"), line));1092}1093}1094update_tabs();1095}1096} break;10971098case NOTIFICATION_THEME_CHANGED: {1099tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));11001101skip_breakpoints->set_button_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));1102ignore_error_breaks->set_button_icon(get_editor_theme_icon(ignore_error_breaks_value ? SNAME("NotificationDisabled") : SNAME("Notification")));1103ignore_error_breaks->add_theme_color_override("icon_normal_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1104ignore_error_breaks->add_theme_color_override("icon_hover_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1105ignore_error_breaks->add_theme_color_override("icon_pressed_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1106ignore_error_breaks->add_theme_color_override("icon_focus_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1107copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));1108step->set_button_icon(get_editor_theme_icon(SNAME("DebugStep")));1109next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext")));1110out->set_button_icon(get_editor_theme_icon(SNAME("DebugOut")));1111dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause")));1112docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue")));1113vmem_notice_icon->set_texture(get_editor_theme_icon(SNAME("NodeInfo")));1114vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload")));1115vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));1116vmem_item_menu->set_item_icon(VMEM_MENU_SHOW_IN_FILESYSTEM, get_editor_theme_icon(SNAME("ShowInFileSystem")));1117vmem_item_menu->set_item_icon(VMEM_MENU_SHOW_IN_EXPLORER, get_editor_theme_icon(SNAME("Filesystem")));1118search->set_right_icon(get_editor_theme_icon(SNAME("Search")));11191120reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1121reason->add_theme_style_override(SNAME("normal"), get_theme_stylebox(SNAME("normal"), SNAME("Label"))); // Empty stylebox.11221123const Ref<Font> source_font = get_theme_font(SNAME("output_source"), EditorStringName(EditorFonts));1124if (source_font.is_valid()) {1125error_tree->add_theme_font_override("font", source_font);1126}1127const int font_size = get_theme_font_size(SNAME("output_source_size"), EditorStringName(EditorFonts));1128error_tree->add_theme_font_size_override("font_size", font_size);11291130TreeItem *error_root = error_tree->get_root();1131if (error_root) {1132TreeItem *error = error_root->get_first_child();1133while (error) {1134if (error->has_meta("_is_warning")) {1135error->set_icon(0, get_editor_theme_icon(SNAME("Warning")));1136error->set_custom_color(0, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));1137error->set_custom_color(1, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));1138} else if (error->has_meta("_is_error")) {1139error->set_icon(0, get_editor_theme_icon(SNAME("Error")));1140error->set_custom_color(0, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1141error->set_custom_color(1, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1142}11431144error = error->get_next();1145}1146}1147} break;11481149case NOTIFICATION_PROCESS: {1150if (is_session_active()) {1151peer->poll();11521153if (camera_override == CameraOverride::OVERRIDE_EDITORS) {1154// CanvasItem Editor1155{1156Dictionary state = CanvasItemEditor::get_singleton()->get_state();1157float zoom = state["zoom"];1158Point2 offset = state["ofs"];1159Transform2D transform;11601161transform.scale_basis(Size2(zoom, zoom));1162transform.columns[2] = -offset * zoom;11631164Array msg = { transform };1165_put_msg("scene:transform_camera_2d", msg);1166}11671168// Node3D Editor1169{1170Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport();1171const Camera3D *cam = viewport->get_camera_3d();11721173Array msg = { cam->get_camera_transform() };1174if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {1175msg.push_back(false);1176msg.push_back(cam->get_size());1177} else {1178msg.push_back(true);1179msg.push_back(cam->get_fov());1180}1181msg.push_back(cam->get_near());1182msg.push_back(cam->get_far());1183_put_msg("scene:transform_camera_3d", msg);1184}1185}11861187if (is_breaked() && can_request_idle_draw) {1188_put_msg("servers:draw", Array());1189can_request_idle_draw = false;1190}1191}11921193const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20;11941195while (peer.is_valid() && peer->has_message()) {1196Array arr = peer->get_message();1197if (arr.size() != 3 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::INT || arr[2].get_type() != Variant::ARRAY) {1198_stop_and_notify();1199ERR_FAIL_MSG("Invalid message format received from peer");1200}12011202_parse_message(arr[0], arr[1], arr[2]);12031204if (OS::get_singleton()->get_ticks_msec() > until) {1205break;1206}1207}1208if (!is_session_active()) {1209_stop_and_notify();1210break;1211};1212} break;1213}1214}12151216void ScriptEditorDebugger::_clear_execution() {1217TreeItem *ti = stack_dump->get_selected();1218if (!ti) {1219return;1220}12211222Dictionary d = ti->get_metadata(0);12231224stack_script = ResourceLoader::load(d["file"]);1225emit_signal(SNAME("clear_execution"), stack_script);1226stack_script.unref();1227stack_dump->clear();1228inspector->clear_stack_variables();1229}12301231void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) {1232Ref<Script> scr = ResourceLoader::load(p_file);1233emit_signal(SNAME("set_breakpoint"), scr, p_line - 1, p_enabled);1234scr.unref();1235}12361237void ScriptEditorDebugger::_clear_breakpoints() {1238emit_signal(SNAME("clear_breakpoints"));1239}12401241void ScriptEditorDebugger::_breakpoint_tree_clicked() {1242TreeItem *selected = breakpoints_tree->get_selected();1243if (selected->has_meta("line")) {1244emit_signal(SNAME("breakpoint_selected"), selected->get_parent()->get_text(0), int(selected->get_meta("line")));1245}1246}12471248String ScriptEditorDebugger::_format_frame_text(const ScriptLanguage::StackInfo *info) {1249String text = info->file.get_file() + ":" + itos(info->line) + " @ " + info->func;1250if (!text.ends_with(")")) {1251text += "()";1252}1253return text;1254}12551256void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {1257_clear_errors_list();1258stop();12591260profiler->set_enabled(true, true);1261visual_profiler->set_enabled(true);12621263peer = p_peer;1264ERR_FAIL_COND(p_peer.is_null());12651266performance_profiler->reset();12671268set_process(true);1269camera_override = CameraOverride::OVERRIDE_NONE;12701271_set_reason_text(TTRC("Debug session started."), MESSAGE_SUCCESS);1272_update_buttons_state();12731274Array quit_keys = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));1275_put_msg("scene:setup_scene", quit_keys);12761277if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {1278profiler->set_profiling(true);1279}12801281if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {1282visual_profiler->set_profiling(true);1283}1284}12851286void ScriptEditorDebugger::_update_buttons_state() {1287const bool active = is_session_active();1288const bool has_editor_tree = active && editor_remote_tree && editor_remote_tree->get_selected();1289vmem_refresh->set_disabled(!active);1290step->set_disabled(!active || !is_breaked() || !is_debuggable());1291next->set_disabled(!active || !is_breaked() || !is_debuggable());1292out->set_disabled(!active || !is_breaked() || !is_debuggable());1293copy->set_disabled(!active || !is_breaked());1294docontinue->set_disabled(!active || !is_breaked());1295dobreak->set_disabled(!active || is_breaked());1296le_clear->set_disabled(!active);1297le_set->set_disabled(!has_editor_tree);12981299thread_list_updating = true;1300LocalVector<ThreadDebugged *> threadss;1301for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {1302threadss.push_back(&I.value);1303}1304threads->set_disabled(threadss.is_empty());13051306threadss.sort_custom<ThreadSort>();1307threads->clear();1308int32_t selected_index = -1;1309for (uint32_t i = 0; i < threadss.size(); i++) {1310if (debugging_thread_id == threadss[i]->thread_id) {1311selected_index = i;1312}1313threads->add_item(threadss[i]->name);1314threads->set_item_metadata(threads->get_item_count() - 1, threadss[i]->thread_id);1315}1316if (selected_index != -1) {1317threads->select(selected_index);1318}13191320thread_list_updating = false;1321}13221323void ScriptEditorDebugger::_stop_and_notify() {1324stop();1325emit_signal(SNAME("stopped"));1326_set_reason_text(TTRC("Debug session closed."), MESSAGE_WARNING);1327}13281329void ScriptEditorDebugger::stop() {1330set_process(false);1331threads_debugged.clear();1332debugging_thread_id = Thread::UNASSIGNED_ID;1333remote_pid = 0;1334_clear_execution();13351336inspector->clear_cache();13371338if (peer.is_valid()) {1339peer->close();1340peer.unref();1341reason->set_text("");1342reason->set_tooltip_text("");1343reason->set_custom_minimum_size(Size2(0, 0));1344}13451346node_path_cache.clear();1347res_path_cache.clear();1348profiler_signature.clear();13491350profiler->set_enabled(false, false);1351profiler->set_profiling(false);13521353visual_profiler->set_enabled(false);1354visual_profiler->set_profiling(false);13551356inspector->edit(nullptr);1357_update_buttons_state();1358}13591360void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {1361Array msg_data = { p_enable };1362switch (p_type) {1363case PROFILER_VISUAL:1364_put_msg("profiler:visual", msg_data);1365break;1366case PROFILER_SCRIPTS_SERVERS:1367if (p_enable) {1368// Clear old script signatures. (should we move all this into the profiler?)1369profiler_signature.clear();1370// Add max funcs options to request.1371int max_funcs = EDITOR_GET("debugger/profiler_frame_max_functions");1372bool include_native = EDITOR_GET("debugger/profile_native_calls");1373Array opts = { CLAMP(max_funcs, 16, 512), include_native };1374msg_data.push_back(opts);1375}1376_put_msg("profiler:servers", msg_data);1377break;1378default:1379ERR_FAIL_MSG("Invalid profiler type");1380}1381}13821383void ScriptEditorDebugger::_profiler_seeked() {1384if (is_breaked()) {1385return;1386}1387debug_break();1388}13891390void ScriptEditorDebugger::_stack_dump_frame_selected() {1391emit_signal(SNAME("stack_frame_selected"));13921393int frame = get_stack_script_frame();13941395if (!request_stack_dump(frame)) {1396inspector->edit(nullptr);1397}1398}13991400void ScriptEditorDebugger::_export_csv() {1401file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);1402file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);1403file_dialog_purpose = SAVE_MONITORS_CSV;1404file_dialog->popup_file_dialog();1405}14061407String ScriptEditorDebugger::get_var_value(const String &p_var) const {1408if (!is_breaked()) {1409return String();1410}1411return inspector->get_stack_variable(p_var);1412}14131414void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_resources) {1415Array msg = { p_resources };1416_put_msg("scene:reload_cached_files", msg);1417}14181419int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {1420const int *r = node_path_cache.getptr(p_path);1421if (r) {1422return *r;1423}14241425last_path_id++;14261427node_path_cache[p_path] = last_path_id;1428Array msg = { p_path, last_path_id };1429_put_msg("scene:live_node_path", msg);14301431return last_path_id;1432}14331434int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) {1435HashMap<String, int>::Iterator E = res_path_cache.find(p_path);14361437if (E) {1438return E->value;1439}14401441last_path_id++;14421443res_path_cache[p_path] = last_path_id;1444Array msg = { p_path, last_path_id };1445_put_msg("scene:live_res_path", msg);14461447return last_path_id;1448}14491450void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {1451if (!p_base || !live_debug || !is_session_active() || !EditorNode::get_singleton()->get_edited_scene()) {1452return;1453}14541455Node *node = Object::cast_to<Node>(p_base);14561457for (int i = 0; i < p_argcount; i++) {1458//no pointers, sorry1459if (p_args[i]->get_type() == Variant::OBJECT || p_args[i]->get_type() == Variant::RID) {1460return;1461}1462}14631464if (node) {1465NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node);1466int pathid = _get_node_path_cache(path);14671468Array msg = { pathid, p_name };1469for (int i = 0; i < p_argcount; i++) {1470//no pointers, sorry1471msg.push_back(*p_args[i]);1472}1473_put_msg("scene:live_node_call", msg);14741475return;1476}14771478Resource *res = Object::cast_to<Resource>(p_base);14791480if (res && !res->get_path().is_empty()) {1481String respath = res->get_path();1482int pathid = _get_res_path_cache(respath);14831484Array msg = { pathid, p_name };1485for (int i = 0; i < p_argcount; i++) {1486//no pointers, sorry1487msg.push_back(*p_args[i]);1488}1489_put_msg("scene:live_res_call", msg);14901491return;1492}1493}14941495void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p_property, const Variant &p_value) {1496if (!p_base || !live_debug || !EditorNode::get_singleton()->get_edited_scene()) {1497return;1498}14991500Node *node = Object::cast_to<Node>(p_base);15011502if (node) {1503NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node);1504int pathid = _get_node_path_cache(path);15051506if (p_value.is_ref_counted()) {1507Ref<Resource> res = p_value;1508if (res.is_valid() && !res->get_path().is_empty()) {1509Array msg = { pathid, p_property, res->get_path() };1510_put_msg("scene:live_node_prop_res", msg);1511}1512} else {1513Array msg = { pathid, p_property, p_value };1514_put_msg("scene:live_node_prop", msg);1515}15161517return;1518}15191520Resource *res = Object::cast_to<Resource>(p_base);15211522if (res && !res->get_path().is_empty()) {1523String respath = res->get_path();1524int pathid = _get_res_path_cache(respath);15251526if (p_value.is_ref_counted()) {1527Ref<Resource> res2 = p_value;1528if (res2.is_valid() && !res2->get_path().is_empty()) {1529Array msg = { pathid, p_property, res2->get_path() };1530_put_msg("scene:live_res_prop_res", msg);1531}1532} else {1533Array msg = { pathid, p_property, p_value };1534_put_msg("scene:live_res_prop", msg);1535}15361537return;1538}1539}15401541bool ScriptEditorDebugger::is_move_to_foreground() const {1542return move_to_foreground;1543}15441545void ScriptEditorDebugger::set_move_to_foreground(const bool &p_move_to_foreground) {1546move_to_foreground = p_move_to_foreground;1547}15481549String ScriptEditorDebugger::get_stack_script_file() const {1550TreeItem *ti = stack_dump->get_selected();1551if (!ti) {1552return "";1553}1554Dictionary d = ti->get_metadata(0);1555return d["file"];1556}15571558int ScriptEditorDebugger::get_stack_script_line() const {1559TreeItem *ti = stack_dump->get_selected();1560if (!ti) {1561return -1;1562}1563Dictionary d = ti->get_metadata(0);1564return d["line"];1565}15661567int ScriptEditorDebugger::get_stack_script_frame() const {1568TreeItem *ti = stack_dump->get_selected();1569if (!ti) {1570return -1;1571}1572Dictionary d = ti->get_metadata(0);1573return d["frame"];1574}15751576bool ScriptEditorDebugger::request_stack_dump(const int &p_frame) {1577ERR_FAIL_COND_V(!is_session_active() || p_frame < 0, false);15781579Array msg = { p_frame };1580_put_msg("get_stack_frame_vars", msg, debugging_thread_id);1581return true;1582}15831584void ScriptEditorDebugger::set_live_debugging(bool p_enable) {1585live_debug = p_enable;1586}15871588void ScriptEditorDebugger::_live_edit_set() {1589if (!is_session_active() || !editor_remote_tree) {1590return;1591}15921593TreeItem *ti = editor_remote_tree->get_selected();1594if (!ti) {1595return;1596}15971598String path;15991600while (ti) {1601String lp = ti->get_text(0);1602path = "/" + lp + path;1603ti = ti->get_parent();1604}16051606NodePath np = path;16071608EditorNode::get_editor_data().set_edited_scene_live_edit_root(np);16091610update_live_edit_root();1611}16121613void ScriptEditorDebugger::_live_edit_clear() {1614NodePath np = NodePath("/root");1615EditorNode::get_editor_data().set_edited_scene_live_edit_root(np);16161617update_live_edit_root();1618}16191620void ScriptEditorDebugger::update_live_edit_root() {1621NodePath np = EditorNode::get_editor_data().get_edited_scene_live_edit_root();16221623Array msg = { np };1624if (EditorNode::get_singleton()->get_edited_scene()) {1625msg.push_back(EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path());1626} else {1627msg.push_back("");1628}1629_put_msg("scene:live_set_root", msg);1630live_edit_root->set_text(String(np));1631}16321633void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {1634if (live_debug) {1635Array msg = { p_parent, p_type, p_name };1636_put_msg("scene:live_create_node", msg);1637}1638}16391640void ScriptEditorDebugger::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) {1641if (live_debug) {1642Array msg = { p_parent, p_path, p_name };1643_put_msg("scene:live_instantiate_node", msg);1644}1645}16461647void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {1648if (live_debug) {1649Array msg = { p_at };1650_put_msg("scene:live_remove_node", msg);1651}1652}16531654void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {1655if (live_debug) {1656Array msg = { p_at, p_keep_id };1657_put_msg("scene:live_remove_and_keep_node", msg);1658}1659}16601661void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {1662if (live_debug) {1663Array msg = { p_id, p_at, p_at_pos };1664_put_msg("scene:live_restore_node", msg);1665}1666}16671668void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {1669if (live_debug) {1670Array msg = { p_at, p_new_name };1671_put_msg("scene:live_duplicate_node", msg);1672}1673}16741675void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {1676if (live_debug) {1677Array msg = { p_at, p_new_place, p_new_name, p_at_pos };1678_put_msg("scene:live_reparent_node", msg);1679}1680}16811682bool ScriptEditorDebugger::get_debug_mute_audio() const {1683return debug_mute_audio;1684}16851686void ScriptEditorDebugger::_send_debug_mute_audio_msg(bool p_mute) {1687Array msg = { p_mute };1688_put_msg("scene:debug_mute_audio", msg);1689}16901691void ScriptEditorDebugger::set_debug_mute_audio(bool p_mute) {1692// Send the message if we want to mute the audio or if it isn't muted already due to a break.1693if (p_mute || !audio_muted_on_break) {1694_send_debug_mute_audio_msg(p_mute);1695}1696debug_mute_audio = p_mute;1697}16981699void ScriptEditorDebugger::_mute_audio_on_break(bool p_mute) {1700// Send the message if we want to mute the audio on a break or if it isn't muted already.1701if (p_mute || !debug_mute_audio) {1702_send_debug_mute_audio_msg(p_mute);1703}1704audio_muted_on_break = p_mute;1705}17061707CameraOverride ScriptEditorDebugger::get_camera_override() const {1708return camera_override;1709}17101711void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {1712Array msg = {1713p_override != CameraOverride::OVERRIDE_NONE,1714p_override == CameraOverride::OVERRIDE_EDITORS1715};1716_put_msg("scene:override_cameras", msg);17171718camera_override = p_override;1719}17201721void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {1722Array msg = { p_path, p_line, p_enabled };1723_put_msg("breakpoint", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);17241725TreeItem *path_item = breakpoints_tree->search_item_text(p_path);1726if (path_item == nullptr) {1727if (!p_enabled) {1728return;1729}1730path_item = breakpoints_tree->create_item();1731path_item->set_text(0, p_path);1732}17331734int idx = 0;1735TreeItem *breakpoint_item;1736for (breakpoint_item = path_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {1737if ((int)breakpoint_item->get_meta("line") < p_line) {1738idx++;1739continue;1740}17411742if ((int)breakpoint_item->get_meta("line") == p_line) {1743break;1744}1745}17461747if (breakpoint_item == nullptr) {1748if (!p_enabled) {1749return;1750}1751breakpoint_item = breakpoints_tree->create_item(path_item, idx);1752breakpoint_item->set_meta("line", p_line);1753breakpoint_item->set_text(0, vformat(TTR("Line %d"), p_line));1754return;1755}17561757if (!p_enabled) {1758path_item->remove_child(breakpoint_item);1759if (path_item->get_first_child() == nullptr) {1760breakpoints_tree->get_root()->remove_child(path_item);1761}1762}1763}17641765void ScriptEditorDebugger::reload_all_scripts() {1766_put_msg("reload_all_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);1767}17681769void ScriptEditorDebugger::reload_scripts(const Vector<String> &p_script_paths) {1770_put_msg("reload_scripts", Variant(p_script_paths).operator Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);1771}17721773bool ScriptEditorDebugger::is_skip_breakpoints() const {1774return skip_breakpoints_value;1775}17761777bool ScriptEditorDebugger::is_ignore_error_breaks() const {1778return ignore_error_breaks_value;1779}17801781void ScriptEditorDebugger::_error_activated() {1782TreeItem *selected = error_tree->get_selected();17831784if (!selected) {1785return;1786}17871788TreeItem *ci = selected->get_first_child();1789if (ci) {1790selected->set_collapsed(!selected->is_collapsed());1791}1792}17931794void ScriptEditorDebugger::_error_selected() {1795TreeItem *selected = error_tree->get_selected();17961797if (!selected) {1798return;1799}18001801Array meta = selected->get_metadata(0);1802if (meta.is_empty()) {1803return;1804}18051806emit_signal(SNAME("error_selected"), String(meta[0]), int(meta[1]));1807}18081809void ScriptEditorDebugger::_expand_errors_list() {1810TreeItem *root = error_tree->get_root();1811if (!root) {1812return;1813}18141815TreeItem *item = root->get_first_child();1816while (item) {1817item->set_collapsed(false);1818item = item->get_next();1819}1820}18211822void ScriptEditorDebugger::_collapse_errors_list() {1823TreeItem *root = error_tree->get_root();1824if (!root) {1825return;1826}18271828TreeItem *item = root->get_first_child();1829while (item) {1830item->set_collapsed(true);1831item = item->get_next();1832}1833}18341835void ScriptEditorDebugger::_vmem_item_activated() {1836TreeItem *selected = vmem_tree->get_selected();1837if (!selected) {1838return;1839}1840const String path = selected->get_text(0);1841if (path.is_empty() || !FileAccess::exists(path)) {1842return;1843}1844FileSystemDock::get_singleton()->navigate_to_path(path);1845}18461847void ScriptEditorDebugger::_vmem_tree_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {1848if (p_button != MouseButton::RIGHT) {1849return;1850}18511852TreeItem *item = vmem_tree->get_selected();1853if (!item) {1854return;1855}18561857String path = item->get_text(0);1858if (path.is_empty() || !FileAccess::exists(path)) {1859return;1860}18611862vmem_item_menu->set_position(vmem_tree->get_screen_position() + p_pos);1863vmem_item_menu->popup();1864}18651866void ScriptEditorDebugger::_vmem_item_menu_id_pressed(int p_option) {1867TreeItem *item = vmem_tree->get_selected();1868if (!item) {1869return;1870}18711872String path = item->get_text(0);1873switch (p_option) {1874case VMEM_MENU_SHOW_IN_FILESYSTEM: {1875FileSystemDock::get_singleton()->navigate_to_path(path);1876} break;1877case VMEM_MENU_SHOW_IN_EXPLORER: {1878OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(path), true);1879} break;1880case VMEM_MENU_OWNERS: {1881FileSystemDock::get_owners_dialog()->show(path);1882} break;1883}1884}18851886void ScriptEditorDebugger::_clear_errors_list() {1887error_tree->clear();1888error_count = 0;1889warning_count = 0;1890emit_signal(SNAME("errors_cleared"));1891update_tabs();18921893expand_all_button->set_disabled(true);1894collapse_all_button->set_disabled(true);1895clear_button->set_disabled(true);1896}18971898void ScriptEditorDebugger::_breakpoints_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {1899if (p_button != MouseButton::RIGHT) {1900return;1901}19021903breakpoints_menu->clear();1904breakpoints_menu->set_size(Size2(1, 1));19051906const TreeItem *selected = breakpoints_tree->get_selected();1907String file = selected->get_text(0);1908if (selected->has_meta("line")) {1909breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTRC("Delete Breakpoint"), ACTION_DELETE_BREAKPOINT);1910file = selected->get_parent()->get_text(0);1911}1912breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete All Breakpoints in:") + " " + file, ACTION_DELETE_BREAKPOINTS_IN_FILE);1913breakpoints_menu->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_DISABLED);1914breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTRC("Delete All Breakpoints"), ACTION_DELETE_ALL_BREAKPOINTS);19151916breakpoints_menu->set_position(get_screen_position() + get_local_mouse_position());1917breakpoints_menu->popup();1918}19191920// Right click on specific file(s) or folder(s).1921void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {1922if (p_button != MouseButton::RIGHT) {1923return;1924}19251926item_menu->clear();1927item_menu->reset_size();19281929if (error_tree->is_anything_selected()) {1930item_menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCopy")), TTRC("Copy Error"), ACTION_COPY_ERROR);1931item_menu->add_icon_item(get_editor_theme_icon(SNAME("ExternalLink")), TTRC("Open C++ Source on GitHub"), ACTION_OPEN_SOURCE);1932}19331934if (item_menu->get_item_count() > 0) {1935item_menu->set_position(error_tree->get_screen_position() + p_pos);1936item_menu->popup();1937}1938}19391940void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {1941switch (p_option) {1942case ACTION_COPY_ERROR: {1943TreeItem *ti = error_tree->get_selected();1944while (ti->get_parent() != error_tree->get_root()) {1945ti = ti->get_parent();1946}19471948String type;19491950if (ti->has_meta("_is_warning")) {1951type = "W ";1952} else if (ti->has_meta("_is_error")) {1953type = "E ";1954}19551956String text = ti->get_text(0) + " ";1957int rpad_len = text.length();19581959text = type + text + ti->get_text(1) + "\n";1960TreeItem *ci = ti->get_first_child();1961while (ci) {1962text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";1963ci = ci->get_next();1964}19651966DisplayServer::get_singleton()->clipboard_set(text);1967} break;19681969case ACTION_OPEN_SOURCE: {1970TreeItem *ti = error_tree->get_selected();1971while (ti->get_parent() != error_tree->get_root()) {1972ti = ti->get_parent();1973}19741975// Find the child with the "C++ Source".1976// It's not at a fixed position as "C++ Error" may come first.1977TreeItem *ci = ti->get_first_child();1978const String cpp_source = "<" + TTR("C++ Source") + ">";1979while (ci) {1980if (ci->get_text(0) == cpp_source) {1981break;1982}1983ci = ci->get_next();1984}19851986if (!ci) {1987WARN_PRINT_ED("No C++ source reference is available for this error.");1988return;1989}19901991// Parse back the `file:line @ method()` string.1992const Vector<String> file_line_number = ci->get_text(1).get_slicec('@', 0).strip_edges().split(":");1993ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report).");1994const String &file = file_line_number[0];1995const int line_number = file_line_number[1].to_int();19961997// Construct a GitHub repository URL and open it in the user's default web browser.1998// If the commit hash is available, use it for greater accuracy. Otherwise fall back to tagged release.1999String git_ref = String(GODOT_VERSION_HASH).is_empty() ? String(GODOT_VERSION_NUMBER) + "-stable" : String(GODOT_VERSION_HASH);2000OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d",2001git_ref, file, line_number));2002} break;2003case ACTION_DELETE_BREAKPOINT: {2004const TreeItem *selected = breakpoints_tree->get_selected();2005_set_breakpoint(selected->get_parent()->get_text(0), selected->get_meta("line"), false);2006} break;2007case ACTION_DELETE_BREAKPOINTS_IN_FILE: {2008TreeItem *file_item = breakpoints_tree->get_selected();2009if (file_item->has_meta("line")) {2010file_item = file_item->get_parent();2011}20122013// Store first else we will be removing as we loop.2014List<int> lines;2015for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {2016lines.push_back(breakpoint_item->get_meta("line"));2017}20182019for (const int &line : lines) {2020_set_breakpoint(file_item->get_text(0), line, false);2021}2022} break;2023case ACTION_DELETE_ALL_BREAKPOINTS: {2024_clear_breakpoints();2025} break;2026}2027}20282029void ScriptEditorDebugger::_tab_changed(int p_tab) {2030if (tabs->get_tab_title(p_tab) == "Video RAM") {2031// "Video RAM" tab was clicked, refresh the data it's displaying when entering the tab.2032_video_mem_request();2033}2034}20352036void ScriptEditorDebugger::_bind_methods() {2037ClassDB::bind_method(D_METHOD("live_debug_create_node"), &ScriptEditorDebugger::live_debug_create_node);2038ClassDB::bind_method(D_METHOD("live_debug_instantiate_node"), &ScriptEditorDebugger::live_debug_instantiate_node);2039ClassDB::bind_method(D_METHOD("live_debug_remove_node"), &ScriptEditorDebugger::live_debug_remove_node);2040ClassDB::bind_method(D_METHOD("live_debug_remove_and_keep_node"), &ScriptEditorDebugger::live_debug_remove_and_keep_node);2041ClassDB::bind_method(D_METHOD("live_debug_restore_node"), &ScriptEditorDebugger::live_debug_restore_node);2042ClassDB::bind_method(D_METHOD("live_debug_duplicate_node"), &ScriptEditorDebugger::live_debug_duplicate_node);2043ClassDB::bind_method(D_METHOD("live_debug_reparent_node"), &ScriptEditorDebugger::live_debug_reparent_node);2044ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value", "field"), &ScriptEditorDebugger::update_remote_object);20452046ADD_SIGNAL(MethodInfo("started"));2047ADD_SIGNAL(MethodInfo("stopped"));2048ADD_SIGNAL(MethodInfo("stop_requested"));2049ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));2050ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error")));2051ADD_SIGNAL(MethodInfo("breakpoint_selected", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));2052ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));2053ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));2054ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"), PropertyInfo(Variant::STRING, "reason"), PropertyInfo(Variant::BOOL, "has_stackdump")));2055ADD_SIGNAL(MethodInfo("remote_objects_requested", PropertyInfo(Variant::ARRAY, "ids")));2056ADD_SIGNAL(MethodInfo("remote_objects_updated", PropertyInfo(Variant::OBJECT, "remote_objects")));2057ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));2058ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title")));2059ADD_SIGNAL(MethodInfo("remote_tree_updated"));2060ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::ARRAY, "ids")));2061ADD_SIGNAL(MethodInfo("remote_tree_clear_selection_requested"));2062ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));2063ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));2064ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));2065ADD_SIGNAL(MethodInfo("stack_frame_var", PropertyInfo(Variant::ARRAY, "data")));2066ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data")));2067ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));2068ADD_SIGNAL(MethodInfo("clear_breakpoints"));2069ADD_SIGNAL(MethodInfo("errors_cleared"));2070ADD_SIGNAL(MethodInfo("embed_shortcut_requested", PropertyInfo(Variant::INT, "embed_shortcut_action")));2071}20722073void ScriptEditorDebugger::add_debugger_tab(Control *p_control) {2074tabs->add_child(p_control);2075}20762077void ScriptEditorDebugger::remove_debugger_tab(Control *p_control) {2078int idx = tabs->get_tab_idx_from_control(p_control);2079ERR_FAIL_COND(idx < 0);2080p_control->queue_free();2081}20822083int ScriptEditorDebugger::get_current_debugger_tab() const {2084return tabs->get_current_tab();2085}20862087void ScriptEditorDebugger::switch_to_debugger(int p_debugger_tab_idx) {2088tabs->set_current_tab(p_debugger_tab_idx);2089}20902091void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) {2092_put_msg(p_message, p_args);2093}20942095void ScriptEditorDebugger::toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data) {2096Array msg_data = { p_enable, p_data };2097_put_msg("profiler:" + p_profiler, msg_data);2098}20992100ScriptEditorDebugger::ScriptEditorDebugger() {2101if (unlikely(parse_message_handlers.is_empty())) {2102_init_parse_message_handlers();2103}21042105tabs = memnew(TabContainer);2106add_child(tabs);2107tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed));21082109InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));2110EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &ScriptEditorDebugger::_resources_reimported));21112112{ // Debugger.2113VBoxContainer *vbc = memnew(VBoxContainer);2114vbc->set_name(TTRC("Stack Trace"));2115Control *dbg = vbc;21162117HBoxContainer *hbc = memnew(HBoxContainer);2118vbc->add_child(hbc);21192120reason = memnew(RichTextLabel);2121reason->set_focus_mode(FOCUS_ACCESSIBILITY);2122reason->set_selection_enabled(true);2123reason->set_context_menu_enabled(true);2124reason->set_h_size_flags(SIZE_EXPAND_FILL);2125reason->set_v_size_flags(SIZE_SHRINK_CENTER);2126reason->connect(SceneStringName(resized), callable_mp(this, &ScriptEditorDebugger::_update_reason_content_height));2127hbc->add_child(reason);21282129hbc->add_child(memnew(VSeparator));21302131skip_breakpoints = memnew(Button);2132skip_breakpoints->set_theme_type_variation(SceneStringName(FlatButton));2133hbc->add_child(skip_breakpoints);2134skip_breakpoints->set_tooltip_text(TTRC("Skip Breakpoints"));2135skip_breakpoints->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints));21362137ignore_error_breaks = memnew(Button);2138ignore_error_breaks->set_theme_type_variation(SceneStringName(FlatButton));2139ignore_error_breaks->set_tooltip_text(TTRC("Ignore Error Breaks"));2140hbc->add_child(ignore_error_breaks);2141ignore_error_breaks->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_ignore_error_breaks));21422143hbc->add_child(memnew(VSeparator));21442145copy = memnew(Button);2146copy->set_theme_type_variation(SceneStringName(FlatButton));2147hbc->add_child(copy);2148copy->set_tooltip_text(TTRC("Copy Error"));2149copy->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_copy));21502151hbc->add_child(memnew(VSeparator));21522153step = memnew(Button);2154step->set_theme_type_variation(SceneStringName(FlatButton));2155hbc->add_child(step);2156step->set_tooltip_text(TTRC("Step Into"));2157step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into"));2158step->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_step));21592160next = memnew(Button);2161next->set_theme_type_variation(SceneStringName(FlatButton));2162hbc->add_child(next);2163next->set_tooltip_text(TTRC("Step Over"));2164next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over"));2165next->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_next));21662167out = memnew(Button);2168out->set_theme_type_variation(SceneStringName(FlatButton));2169hbc->add_child(out);2170out->set_tooltip_text(TTRC("Step Out"));2171out->set_shortcut(ED_GET_SHORTCUT("debugger/step_out"));2172out->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_out));21732174hbc->add_child(memnew(VSeparator));21752176dobreak = memnew(Button);2177dobreak->set_theme_type_variation(SceneStringName(FlatButton));2178hbc->add_child(dobreak);2179dobreak->set_tooltip_text(TTRC("Break"));2180dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break"));2181dobreak->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_break));21822183docontinue = memnew(Button);2184docontinue->set_theme_type_variation(SceneStringName(FlatButton));2185hbc->add_child(docontinue);2186docontinue->set_tooltip_text(TTRC("Continue"));2187docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));2188docontinue->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_continue));21892190HSplitContainer *sc = memnew(HSplitContainer);2191sc->set_v_size_flags(SIZE_EXPAND_FILL);2192vbc->add_child(sc);21932194VBoxContainer *stack_vb = memnew(VBoxContainer);2195stack_vb->set_h_size_flags(SIZE_EXPAND_FILL);2196sc->add_child(stack_vb);2197HBoxContainer *thread_hb = memnew(HBoxContainer);2198stack_vb->add_child(thread_hb);2199thread_hb->add_child(memnew(Label(TTRC("Thread:"))));2200threads = memnew(OptionButton);2201thread_hb->add_child(threads);2202threads->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);2203threads->set_h_size_flags(SIZE_EXPAND_FILL);2204threads->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_select_thread));22052206stack_dump = memnew(Tree);2207stack_dump->set_custom_minimum_size(Size2(150, 0) * EDSCALE);2208stack_dump->set_allow_reselect(true);2209stack_dump->set_columns(1);2210stack_dump->set_column_titles_visible(true);2211stack_dump->set_column_title(0, TTRC("Stack Frames"));2212stack_dump->set_hide_root(true);2213stack_dump->set_v_size_flags(SIZE_EXPAND_FILL);2214stack_dump->set_theme_type_variation("TreeSecondary");2215stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));2216stack_vb->add_child(stack_dump);22172218VBoxContainer *inspector_vbox = memnew(VBoxContainer);2219inspector_vbox->set_custom_minimum_size(Size2(200, 0) * EDSCALE);2220inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);2221sc->add_child(inspector_vbox);22222223HBoxContainer *tools_hb = memnew(HBoxContainer);2224inspector_vbox->add_child(tools_hb);22252226search = memnew(LineEdit);2227search->set_h_size_flags(Control::SIZE_EXPAND_FILL);2228search->set_placeholder(TTRC("Filter Stack Variables"));2229search->set_accessibility_name(TTRC("Filter Stack Variables"));2230search->set_clear_button_enabled(true);2231tools_hb->add_child(search);22322233inspector = memnew(EditorDebuggerInspector);2234inspector->set_h_size_flags(SIZE_EXPAND_FILL);2235inspector->set_v_size_flags(SIZE_EXPAND_FILL);2236inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);2237inspector->set_read_only(true);2238inspector->connect("object_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));2239inspector->connect("objects_edited", callable_mp(this, &ScriptEditorDebugger::_remote_objects_edited));2240inspector->connect("object_property_updated", callable_mp(this, &ScriptEditorDebugger::_remote_object_property_updated));2241inspector->register_text_enter(search);2242inspector->set_use_filter(true);2243inspector_vbox->add_child(inspector);22442245breakpoints_tree = memnew(Tree);2246breakpoints_tree->set_custom_minimum_size(Size2(100, 0) * EDSCALE);2247breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL);2248breakpoints_tree->set_column_titles_visible(true);2249breakpoints_tree->set_column_title(0, TTRC("Breakpoints"));2250breakpoints_tree->set_allow_reselect(true);2251breakpoints_tree->set_allow_rmb_select(true);2252breakpoints_tree->set_hide_root(true);2253breakpoints_tree->set_theme_type_variation("TreeSecondary");2254breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected));2255breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));2256breakpoints_tree->create_item();22572258sc->add_child(breakpoints_tree);2259tabs->add_child(dbg);22602261breakpoints_menu = memnew(PopupMenu);2262breakpoints_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));2263breakpoints_tree->add_child(breakpoints_menu);2264}22652266{ // Errors.2267errors_tab = memnew(VBoxContainer);2268errors_tab->set_name(TTRC("Errors"));22692270HBoxContainer *error_hbox = memnew(HBoxContainer);2271errors_tab->add_child(error_hbox);22722273expand_all_button = memnew(Button);2274expand_all_button->set_text(TTRC("Expand All"));2275expand_all_button->set_disabled(true);2276expand_all_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_expand_errors_list));2277error_hbox->add_child(expand_all_button);22782279collapse_all_button = memnew(Button);2280collapse_all_button->set_text(TTRC("Collapse All"));2281collapse_all_button->set_disabled(true);2282collapse_all_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_collapse_errors_list));2283error_hbox->add_child(collapse_all_button);22842285Control *space = memnew(Control);2286space->set_h_size_flags(SIZE_EXPAND_FILL);2287error_hbox->add_child(space);22882289clear_button = memnew(Button);2290clear_button->set_text(TTRC("Clear"));2291clear_button->set_h_size_flags(0);2292clear_button->set_disabled(true);2293clear_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_clear_errors_list));2294error_hbox->add_child(clear_button);22952296error_tree = memnew(Tree);2297error_tree->set_columns(2);22982299error_tree->set_column_expand(0, false);2300error_tree->set_column_custom_minimum_width(0, 140);2301error_tree->set_column_clip_content(0, true);23022303error_tree->set_column_expand(1, true);2304error_tree->set_column_clip_content(1, true);23052306error_tree->set_select_mode(Tree::SELECT_ROW);2307error_tree->set_hide_root(true);2308error_tree->set_v_size_flags(SIZE_EXPAND_FILL);2309error_tree->set_allow_rmb_select(true);2310error_tree->set_allow_reselect(true);2311error_tree->set_theme_type_variation("TreeSecondary");2312error_tree->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_error_selected));2313error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));2314error_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_error_tree_item_rmb_selected));2315errors_tab->add_child(error_tree);23162317item_menu = memnew(PopupMenu);2318item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));2319error_tree->add_child(item_menu);23202321tabs->add_child(errors_tab);2322}23232324{ // File dialog2325file_dialog = memnew(EditorFileDialog);2326file_dialog->connect("file_selected", callable_mp(this, &ScriptEditorDebugger::_file_selected));2327add_child(file_dialog);2328}23292330{ // Expression evaluator2331expression_evaluator = memnew(EditorExpressionEvaluator);2332expression_evaluator->set_name(TTRC("Evaluator"));2333expression_evaluator->set_editor_debugger(this);2334tabs->add_child(expression_evaluator);2335}23362337{ //profiler2338profiler = memnew(EditorProfiler);2339profiler->set_name(TTRC("Profiler"));2340tabs->add_child(profiler);2341profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_SCRIPTS_SERVERS));2342profiler->connect("break_request", callable_mp(this, &ScriptEditorDebugger::_profiler_seeked));2343}23442345{ //frame profiler2346visual_profiler = memnew(EditorVisualProfiler);2347visual_profiler->set_name(TTRC("Visual Profiler"));2348tabs->add_child(visual_profiler);2349visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_VISUAL));2350}23512352{ //monitors2353performance_profiler = memnew(EditorPerformanceProfiler);2354tabs->add_child(performance_profiler);2355}23562357{ //vmem inspect2358VBoxContainer *vmem_vb = memnew(VBoxContainer);2359HBoxContainer *vmem_hb = memnew(HBoxContainer);23602361Label *vmlb = memnew(Label(TTRC("List of Video Memory Usage by Resource:")));2362vmlb->set_theme_type_variation("HeaderSmall");2363vmem_hb->add_child(vmlb);23642365{ // Add notice icon.2366vmem_notice_icon = memnew(TextureRect);2367vmem_notice_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);2368vmem_notice_icon->set_h_size_flags(SIZE_SHRINK_CENTER);2369vmem_notice_icon->set_visible(true);2370vmem_notice_icon->set_tooltip_text(TTRC(R"(Notice:2371This tool only reports memory allocations tracked by the engine.2372Therefore, total VRAM usage is inaccurate compared to what the Monitors tab or external tools can report.2373Instead, use the monitors tab to obtain more precise VRAM usage.23742375- Buffer Memory (e.g. GPUParticles) is not tracked.2376- Meshes are not tracked in the Compatibility renderer.)"));2377vmem_hb->add_child(vmem_notice_icon);2378}23792380{ // Add some space to move the rest of the controls to the right.2381Control *space = memnew(Control);2382space->set_h_size_flags(SIZE_EXPAND_FILL);2383vmem_hb->add_child(space);2384}23852386vmem_hb->add_child(memnew(Label(TTRC("Total:"))));2387vmem_total = memnew(LineEdit);2388vmem_total->set_editable(false);2389vmem_total->set_accessibility_name(TTRC("Video RAM Total"));2390vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE);2391vmem_hb->add_child(vmem_total);2392vmem_refresh = memnew(Button);2393vmem_refresh->set_accessibility_name(TTRC("Refresh Video RAM"));2394vmem_refresh->set_theme_type_variation(SceneStringName(FlatButton));2395vmem_hb->add_child(vmem_refresh);2396vmem_export = memnew(Button);2397vmem_export->set_theme_type_variation(SceneStringName(FlatButton));2398vmem_export->set_tooltip_text(TTRC("Export list to a CSV file"));2399vmem_hb->add_child(vmem_export);2400vmem_vb->add_child(vmem_hb);2401vmem_refresh->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_video_mem_request));2402vmem_export->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_video_mem_export));24032404MarginContainer *mc = memnew(MarginContainer);2405mc->set_theme_type_variation("NoBorderBottomPanel");2406mc->set_v_size_flags(SIZE_EXPAND_FILL);2407vmem_vb->add_child(mc);24082409vmem_tree = memnew(Tree);2410vmem_vb->set_name(TTRC("Video RAM"));2411vmem_tree->set_columns(4);2412vmem_tree->set_column_titles_visible(true);2413vmem_tree->set_column_title(0, TTRC("Resource Path"));2414vmem_tree->set_column_expand(0, true);2415vmem_tree->set_column_expand(1, false);2416vmem_tree->set_column_title(1, TTRC("Type"));2417vmem_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);2418vmem_tree->set_column_expand(2, false);2419vmem_tree->set_column_title(2, TTRC("Format"));2420vmem_tree->set_column_custom_minimum_width(2, 150 * EDSCALE);2421vmem_tree->set_column_expand(3, false);2422vmem_tree->set_column_title(3, TTRC("Usage"));2423vmem_tree->set_column_custom_minimum_width(3, 80 * EDSCALE);2424vmem_tree->set_hide_root(true);2425vmem_tree->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTTOM);2426mc->add_child(vmem_tree);2427vmem_tree->set_allow_rmb_select(true);2428vmem_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_vmem_item_activated));2429vmem_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_vmem_tree_rmb_selected));2430tabs->add_child(vmem_vb);24312432vmem_item_menu = memnew(PopupMenu);2433vmem_item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_vmem_item_menu_id_pressed));2434vmem_item_menu->add_item(TTRC("Show in FileSystem"), VMEM_MENU_SHOW_IN_FILESYSTEM);2435vmem_item_menu->add_item(TTRC("Show in File Manager"), VMEM_MENU_SHOW_IN_EXPLORER);2436vmem_item_menu->add_item(TTRC("View Owners..."), VMEM_MENU_OWNERS);2437add_child(vmem_item_menu);2438}24392440{ // misc2441VBoxContainer *misc = memnew(VBoxContainer);2442misc->set_name(TTRC("Misc"));2443tabs->add_child(misc);24442445GridContainer *info_left = memnew(GridContainer);2446info_left->set_columns(2);2447misc->add_child(info_left);2448clicked_ctrl = memnew(LineEdit);2449clicked_ctrl->set_editable(false);2450clicked_ctrl->set_accessibility_name(TTRC("Clicked Control:"));2451clicked_ctrl->set_h_size_flags(SIZE_EXPAND_FILL);2452info_left->add_child(memnew(Label(TTRC("Clicked Control:"))));2453info_left->add_child(clicked_ctrl);2454clicked_ctrl_type = memnew(LineEdit);2455clicked_ctrl_type->set_editable(false);2456clicked_ctrl_type->set_accessibility_name(TTRC("Clicked Control Type:"));2457info_left->add_child(memnew(Label(TTRC("Clicked Control Type:"))));2458info_left->add_child(clicked_ctrl_type);24592460scene_tree = memnew(SceneDebuggerTree);2461live_edit_root = memnew(LineEdit);2462live_edit_root->set_editable(false);2463live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL);2464live_edit_root->set_accessibility_name(TTRC("Live Edit Root:"));24652466{2467HBoxContainer *lehb = memnew(HBoxContainer);2468Label *l = memnew(Label(TTRC("Live Edit Root:")));2469info_left->add_child(l);2470lehb->add_child(live_edit_root);2471le_set = memnew(Button(TTRC("Set From Tree")));2472le_set->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_live_edit_set));2473lehb->add_child(le_set);2474le_clear = memnew(Button(TTRC("Clear")));2475le_clear->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_live_edit_clear));2476lehb->add_child(le_clear);2477info_left->add_child(lehb);2478}24792480misc->add_child(memnew(VSeparator));24812482HBoxContainer *buttons = memnew(HBoxContainer);24832484export_csv = memnew(Button(TTRC("Export measures as CSV")));2485export_csv->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_export_csv));2486buttons->add_child(export_csv);24872488misc->add_child(buttons);2489}24902491msgdialog = memnew(AcceptDialog);2492add_child(msgdialog);24932494camera_override = CameraOverride::OVERRIDE_NONE;2495error_count = 0;2496warning_count = 0;2497_update_buttons_state();2498}24992500ScriptEditorDebugger::~ScriptEditorDebugger() {2501if (peer.is_valid()) {2502peer->close();2503peer.unref();2504}2505memdelete(scene_tree);2506}250725082509