feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -30,6 +30,7 @@
#include "debug_adapter_parser.h"
#include "editor/debugger/debug_adapter/debug_adapter_types.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/script_editor_debugger.h"
#include "editor/export/editor_export_platform.h"
@ -146,7 +147,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
String breakpoint = E->get();
String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://"
String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://"
int line = breakpoint.substr(path.size()).to_int();
DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true);
@ -358,7 +359,7 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) co
}
// If path contains \, it's a Windows path, so we need to convert it to /, and make the drive letter uppercase
if (source.path.contains("\\")) {
if (source.path.contains_char('\\')) {
source.path = source.path.replace("\\", "/");
source.path = source.path.substr(0, 1).to_upper() + source.path.substr(1);
}
@ -442,26 +443,34 @@ Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const {
return Dictionary();
}
Dictionary response = prepare_success_response(p_params), body;
response["body"] = body;
Dictionary args = p_params["arguments"];
int variable_id = args["variablesReference"];
HashMap<int, Array>::Iterator E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id);
if (HashMap<int, Array>::Iterator E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); E) {
Dictionary response = prepare_success_response(p_params);
Dictionary body;
response["body"] = body;
if (E) {
if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsVariableType) {
for (int i = 0; i < E->value.size(); i++) {
Dictionary variable = E->value[i];
variable.erase("type");
}
}
body["variables"] = E ? E->value : Array();
return response;
} else {
return Dictionary();
// If the requested variable is an object, it needs to be requested from the debuggee.
ObjectID object_id = DebugAdapterProtocol::get_singleton()->search_object_id(variable_id);
if (object_id.is_null()) {
return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN);
}
DebugAdapterProtocol::get_singleton()->request_remote_object(object_id);
}
return Dictionary();
}
Dictionary DebugAdapterParser::req_next(const Dictionary &p_params) const {
@ -479,16 +488,27 @@ Dictionary DebugAdapterParser::req_stepIn(const Dictionary &p_params) const {
}
Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const {
Dictionary response = prepare_success_response(p_params), body;
response["body"] = body;
Dictionary args = p_params["arguments"];
String expression = args["expression"];
int frame_id = args.has("frameId") ? static_cast<int>(args["frameId"]) : DebugAdapterProtocol::get_singleton()->_current_frame;
String value = EditorDebuggerNode::get_singleton()->get_var_value(args["expression"]);
body["result"] = value;
body["variablesReference"] = 0;
if (HashMap<String, DAP::Variable>::Iterator E = DebugAdapterProtocol::get_singleton()->eval_list.find(expression); E) {
Dictionary response = prepare_success_response(p_params);
Dictionary body;
response["body"] = body;
return response;
DAP::Variable var = E->value;
body["result"] = var.value;
body["variablesReference"] = var.variablesReference;
// Since an evaluation can alter the state of the debuggee, they are volatile, and should only be used once
DebugAdapterProtocol::get_singleton()->eval_list.erase(E->key);
return response;
} else {
DebugAdapterProtocol::get_singleton()->request_remote_evaluate(expression, frame_id);
}
return Dictionary();
}
Dictionary DebugAdapterParser::req_godot_put_msg(const Dictionary &p_params) const {

View file

@ -47,7 +47,7 @@ private:
_FORCE_INLINE_ bool is_valid_path(const String &p_path) const {
// If path contains \, it's a Windows path, so we need to convert it to /, and check as case-insensitive.
if (p_path.contains("\\")) {
if (p_path.contains_char('\\')) {
String project_path = ProjectSettings::get_singleton()->get_resource_path();
String path = p_path.replace("\\", "/");
return path.containsn(project_path);

View file

@ -33,8 +33,8 @@
#include "core/config/project_settings.h"
#include "core/debugger/debugger_marshalls.h"
#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "editor/debugger/script_editor_debugger.h"
#include "editor/doc_tools.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
@ -186,6 +186,8 @@ void DebugAdapterProtocol::reset_stack_info() {
stackframe_list.clear();
variable_list.clear();
object_list.clear();
object_pending_set.clear();
}
int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
@ -671,12 +673,194 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
variable_list.insert(id, arr);
return id;
}
case Variant::OBJECT: {
// Objects have to be requested from the debuggee. This has do be done
// in a lazy way, as retrieving object properties takes time.
EncodedObjectAsID *encoded_obj = Object::cast_to<EncodedObjectAsID>(p_var);
// Object may be null; in that case, return early.
if (!encoded_obj) {
return 0;
}
// Object may have been already requested.
ObjectID object_id = encoded_obj->get_object_id();
if (object_list.has(object_id)) {
return object_list[object_id];
}
// Queue requesting the object.
int id = variable_id++;
object_list.insert(object_id, id);
return id;
}
default:
// Simple atomic stuff, or too complex to be manipulated
return 0;
}
}
void DebugAdapterProtocol::parse_object(SceneDebuggerObject &p_obj) {
// If the object is not on the pending list, we weren't expecting it. Ignore it.
ObjectID object_id = p_obj.id;
if (!object_pending_set.erase(object_id)) {
return;
}
// Populate DAP::Variable's with the object's properties. These properties will be divided by categories.
Array properties;
Array script_members;
Array script_constants;
Array script_node;
DAP::Variable node_type;
Array node_properties;
for (SceneDebuggerObject::SceneDebuggerProperty &property : p_obj.properties) {
PropertyInfo &info = property.first;
// Script members ("Members/" prefix)
if (info.name.begins_with("Members/")) {
info.name = info.name.trim_prefix("Members/");
script_members.push_back(parse_object_variable(property));
}
// Script constants ("Constants/" prefix)
else if (info.name.begins_with("Constants/")) {
info.name = info.name.trim_prefix("Constants/");
script_constants.push_back(parse_object_variable(property));
}
// Script node ("Node/" prefix)
else if (info.name.begins_with("Node/")) {
info.name = info.name.trim_prefix("Node/");
script_node.push_back(parse_object_variable(property));
}
// Regular categories (with type Variant::NIL)
else if (info.type == Variant::NIL) {
if (!node_properties.is_empty()) {
node_type.value = itos(node_properties.size());
variable_list.insert(node_type.variablesReference, node_properties.duplicate());
properties.push_back(node_type.to_json());
}
node_type.name = info.name;
node_type.type = "Category";
node_type.variablesReference = variable_id++;
node_properties.clear();
}
// Regular properties.
else {
node_properties.push_back(parse_object_variable(property));
}
}
// Add the last category.
if (!node_properties.is_empty()) {
node_type.value = itos(node_properties.size());
variable_list.insert(node_type.variablesReference, node_properties.duplicate());
properties.push_back(node_type.to_json());
}
// Add the script categories, in reverse order to be at the front of the array:
// ( [members; constants; node; category1; category2; ...] )
if (!script_node.is_empty()) {
DAP::Variable node;
node.name = "Node";
node.type = "Category";
node.value = itos(script_node.size());
node.variablesReference = variable_id++;
variable_list.insert(node.variablesReference, script_node);
properties.push_front(node.to_json());
}
if (!script_constants.is_empty()) {
DAP::Variable constants;
constants.name = "Constants";
constants.type = "Category";
constants.value = itos(script_constants.size());
constants.variablesReference = variable_id++;
variable_list.insert(constants.variablesReference, script_constants);
properties.push_front(constants.to_json());
}
if (!script_members.is_empty()) {
DAP::Variable members;
members.name = "Members";
members.type = "Category";
members.value = itos(script_members.size());
members.variablesReference = variable_id++;
variable_list.insert(members.variablesReference, script_members);
properties.push_front(members.to_json());
}
ERR_FAIL_COND(!object_list.has(object_id));
variable_list.insert(object_list[object_id], properties);
}
void DebugAdapterProtocol::parse_evaluation(DebuggerMarshalls::ScriptStackVariable &p_var) {
// If the eval is not on the pending list, we weren't expecting it. Ignore it.
String eval = p_var.name;
if (!eval_pending_list.erase(eval)) {
return;
}
DAP::Variable variable;
variable.name = p_var.name;
variable.value = p_var.value;
variable.type = Variant::get_type_name(p_var.value.get_type());
variable.variablesReference = parse_variant(p_var.value);
eval_list.insert(variable.name, variable);
}
const Variant DebugAdapterProtocol::parse_object_variable(const SceneDebuggerObject::SceneDebuggerProperty &p_property) {
const PropertyInfo &info = p_property.first;
const Variant &value = p_property.second;
DAP::Variable var;
var.name = info.name;
var.type = Variant::get_type_name(info.type);
var.value = value;
var.variablesReference = parse_variant(value);
return var.to_json();
}
ObjectID DebugAdapterProtocol::search_object_id(DAPVarID p_var_id) {
for (const KeyValue<ObjectID, DAPVarID> &E : object_list) {
if (E.value == p_var_id) {
return E.key;
}
}
return ObjectID();
}
bool DebugAdapterProtocol::request_remote_object(const ObjectID &p_object_id) {
// If the object is already on the pending list, we don't need to request it again.
if (object_pending_set.has(p_object_id)) {
return false;
}
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_object(p_object_id);
object_pending_set.insert(p_object_id);
return true;
}
bool DebugAdapterProtocol::request_remote_evaluate(const String &p_eval, int p_stack_frame) {
// If the eval is already on the pending list, we don't need to request it again
if (eval_pending_list.has(p_eval)) {
return false;
}
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_evaluate(p_eval, p_stack_frame);
eval_pending_list.insert(p_eval);
return true;
}
bool DebugAdapterProtocol::process_message(const String &p_text) {
JSON json;
ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!");
@ -966,7 +1150,7 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) {
List<int> scope_ids = stackframe_list.find(frame)->value;
ERR_FAIL_COND(scope_ids.size() != 3);
ERR_FAIL_INDEX(stack_var.type, 3);
ERR_FAIL_INDEX(stack_var.type, 4);
int var_id = scope_ids.get(stack_var.type);
DAP::Variable variable;
@ -986,6 +1170,20 @@ void DebugAdapterProtocol::on_debug_data(const String &p_msg, const Array &p_dat
return;
}
if (p_msg == "scene:inspect_object") {
// An object was requested from the debuggee; parse it.
SceneDebuggerObject remote_obj;
remote_obj.deserialize(p_data);
parse_object(remote_obj);
} else if (p_msg == "evaluation_return") {
// An evaluation was requested from the debuggee; parse it.
DebuggerMarshalls::ScriptStackVariable remote_evaluation;
remote_evaluation.deserialize(p_data);
parse_evaluation(remote_evaluation);
}
notify_custom_data(p_msg, p_data);
}
@ -1049,7 +1247,7 @@ DebugAdapterProtocol::DebugAdapterProtocol() {
debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled));
debugger_node->get_default_debugger()->connect("stopped", callable_mp(this, &DebugAdapterProtocol::on_debug_stopped));
debugger_node->get_default_debugger()->connect("output", callable_mp(this, &DebugAdapterProtocol::on_debug_output));
debugger_node->get_default_debugger()->connect(SceneStringName(output), callable_mp(this, &DebugAdapterProtocol::on_debug_output));
debugger_node->get_default_debugger()->connect("breaked", callable_mp(this, &DebugAdapterProtocol::on_debug_breaked));
debugger_node->get_default_debugger()->connect("stack_dump", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_dump));
debugger_node->get_default_debugger()->connect("stack_frame_vars", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_vars));

View file

@ -31,12 +31,13 @@
#ifndef DEBUG_ADAPTER_PROTOCOL_H
#define DEBUG_ADAPTER_PROTOCOL_H
#include "core/io/stream_peer.h"
#include "core/debugger/debugger_marshalls.h"
#include "core/io/stream_peer_tcp.h"
#include "core/io/tcp_server.h"
#include "debug_adapter_parser.h"
#include "debug_adapter_types.h"
#include "scene/debugger/scene_debugger.h"
#define DAP_MAX_BUFFER_SIZE 4194304 // 4MB
#define DAP_MAX_CLIENTS 8
@ -75,6 +76,8 @@ class DebugAdapterProtocol : public Object {
friend class DebugAdapterParser;
using DAPVarID = int;
private:
static DebugAdapterProtocol *singleton;
DebugAdapterParser *parser = nullptr;
@ -99,6 +102,13 @@ private:
void reset_stack_info();
int parse_variant(const Variant &p_var);
void parse_object(SceneDebuggerObject &p_obj);
const Variant parse_object_variable(const SceneDebuggerObject::SceneDebuggerProperty &p_property);
void parse_evaluation(DebuggerMarshalls::ScriptStackVariable &p_var);
ObjectID search_object_id(DAPVarID p_var_id);
bool request_remote_object(const ObjectID &p_object_id);
bool request_remote_evaluate(const String &p_eval, int p_stack_frame);
bool _initialized = false;
bool _processing_breakpoint = false;
@ -106,7 +116,7 @@ private:
bool _processing_stackdump = false;
int _remaining_vars = 0;
int _current_frame = 0;
uint64_t _request_timeout = 1000;
uint64_t _request_timeout = 5000;
bool _sync_breakpoints = false;
String _current_request;
@ -114,10 +124,16 @@ private:
int breakpoint_id = 0;
int stackframe_id = 0;
int variable_id = 0;
DAPVarID variable_id = 0;
List<DAP::Breakpoint> breakpoint_list;
HashMap<DAP::StackFrame, List<int>, DAP::StackFrame> stackframe_list;
HashMap<int, Array> variable_list;
HashMap<DAPVarID, Array> variable_list;
HashMap<ObjectID, DAPVarID> object_list;
HashSet<ObjectID> object_pending_set;
HashMap<String, DAP::Variable> eval_list;
HashSet<String> eval_pending_list;
public:
friend class DebugAdapterServer;

View file

@ -30,7 +30,6 @@
#include "debug_adapter_server.h"
#include "core/os/os.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
@ -38,6 +37,7 @@
int DebugAdapterServer::port_override = -1;
DebugAdapterServer::DebugAdapterServer() {
// TODO: Move to editor_settings.cpp
_EDITOR_DEF("network/debug_adapter/remote_port", remote_port);
_EDITOR_DEF("network/debug_adapter/request_timeout", protocol._request_timeout);
_EDITOR_DEF("network/debug_adapter/sync_breakpoints", protocol._sync_breakpoints);

View file

@ -151,7 +151,7 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
Variant &var = property.second;
if (pinfo.type == Variant::OBJECT) {
if (var.get_type() == Variant::STRING) {
if (var.is_string()) {
String path = var;
if (path.contains("::")) {
// built-in resource
@ -167,7 +167,7 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
if (debug_obj->get_script() != var) {
debug_obj->set_script(Ref<RefCounted>());
Ref<Script> scr(var);
if (!scr.is_null()) {
if (scr.is_valid()) {
ScriptInstance *scr_instance = scr->placeholder_instance_create(debug_obj);
if (scr_instance) {
debug_obj->set_script_and_instance(var, scr_instance);
@ -223,7 +223,7 @@ Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
return nullptr;
}
void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_offset) {
DebuggerMarshalls::ScriptStackVariable var;
var.deserialize(p_array);
String n = var.name;
@ -248,6 +248,9 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
case 2:
type = "Globals/";
break;
case 3:
type = "Evaluated/";
break;
default:
type = "Unknown/";
}
@ -258,7 +261,15 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
pinfo.hint = h;
pinfo.hint_string = hs;
variables->prop_list.push_back(pinfo);
if ((p_offset == -1) || variables->prop_list.is_empty()) {
variables->prop_list.push_back(pinfo);
} else {
List<PropertyInfo>::Element *current = variables->prop_list.front();
for (int i = 0; i < p_offset; i++) {
current = current->next();
}
variables->prop_list.insert_before(current, pinfo);
}
variables->prop_values[type + n] = v;
variables->update();
edit(variables);

View file

@ -48,7 +48,7 @@ public:
List<PropertyInfo> prop_list;
HashMap<StringName, Variant> prop_values;
ObjectID get_remote_object_id() { return remote_object_id; };
ObjectID get_remote_object_id() { return remote_object_id; }
String get_title();
Variant get_variant(const StringName &p_name);
@ -90,7 +90,7 @@ public:
// Stack Dump variables
String get_stack_variable(const String &p_var);
void add_stack_variable(const Array &p_arr);
void add_stack_variable(const Array &p_arr, int p_offset = -1);
void clear_stack_variables();
};

View file

@ -91,6 +91,10 @@ EditorDebuggerNode::EditorDebuggerNode() {
remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
EditorRunBar::get_singleton()->get_pause_button()->connect(SceneStringName(pressed), callable_mp(this, &EditorDebuggerNode::_paused));
}
@ -105,6 +109,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));
node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));
node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id));
node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id));
node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id));
node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id));
node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));
@ -119,7 +124,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
tabs->add_child(node);
node->set_name("Session " + itos(tabs->get_tab_count()));
node->set_name(vformat(TTR("Session %d"), tabs->get_tab_count()));
if (tabs->get_tab_count() > 1) {
node->clear_style();
tabs->set_tabs_visible(true);
@ -159,9 +164,9 @@ void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_d
} else {
// If the script is built-in, it can be opened only if the scene is loaded in memory.
int i = file.find("::");
int j = file.rfind("(", i);
int j = file.rfind_char('(', i);
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
file = file.substr(j + 1, file.find(")", i) - j - 1);
file = file.substr(j + 1, file.find_char(')', i) - j - 1);
}
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
stack_script = ResourceLoader::load(file);
@ -182,9 +187,9 @@ void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_
} else {
// If the script is built-in, it can be opened only if the scene is loaded in memory.
int i = file.find("::");
int j = file.rfind("(", i);
int j = file.rfind_char('(', i);
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
file = file.substr(j + 1, file.find(")", i) - j - 1);
file = file.substr(j + 1, file.find_char(')', i) - j - 1);
}
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
stack_script = ResourceLoader::load(file);
@ -213,8 +218,8 @@ void EditorDebuggerNode::_bind_methods() {
}
void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {
p_undo_redo->set_method_notify_callback(_method_changeds, this);
p_undo_redo->set_property_notify_callback(_property_changeds, this);
p_undo_redo->set_method_notify_callback(_methods_changed, this);
p_undo_redo->set_property_notify_callback(_properties_changed, this);
}
EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
@ -262,6 +267,10 @@ void EditorDebuggerNode::set_keep_open(bool p_keep_open) {
}
Error EditorDebuggerNode::start(const String &p_uri) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return ERR_UNAVAILABLE;
}
ERR_FAIL_COND_V(!p_uri.contains("://"), ERR_INVALID_PARAMETER);
if (keep_open && current_uri == p_uri && server.is_valid()) {
return OK;
@ -303,7 +312,7 @@ void EditorDebuggerNode::stop(bool p_force) {
});
_break_state_changed();
breakpoints.clear();
EditorUndoRedoManager::get_singleton()->clear_history(false, EditorUndoRedoManager::REMOTE_HISTORY);
EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::REMOTE_HISTORY, false);
set_process(false);
}
@ -330,7 +339,7 @@ void EditorDebuggerNode::_notification(int p_what) {
} break;
case NOTIFICATION_PROCESS: {
if (!server.is_valid()) {
if (server.is_null()) {
return;
}
@ -417,18 +426,18 @@ void EditorDebuggerNode::_update_errors() {
if (error_count == 0 && warning_count == 0) {
debugger_button->set_text(TTR("Debugger"));
debugger_button->remove_theme_color_override(SceneStringName(font_color));
debugger_button->set_icon(Ref<Texture2D>());
debugger_button->set_button_icon(Ref<Texture2D>());
} else {
debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
if (error_count >= 1 && warning_count >= 1) {
debugger_button->set_icon(get_editor_theme_icon(SNAME("ErrorWarning")));
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("ErrorWarning")));
// Use error color to represent the highest level of severity reported.
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
} else if (error_count >= 1) {
debugger_button->set_icon(get_editor_theme_icon(SNAME("Error")));
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Error")));
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
} else {
debugger_button->set_icon(get_editor_theme_icon(SNAME("Warning")));
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Warning")));
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
}
}
@ -637,6 +646,13 @@ void EditorDebuggerNode::request_remote_tree() {
get_current_debugger()->request_remote_tree();
}
void EditorDebuggerNode::_remote_tree_select_requested(ObjectID p_id, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
remote_scene_tree->select_node(p_id);
}
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
@ -720,7 +736,7 @@ void EditorDebuggerNode::_breakpoints_cleared_in_tree(int p_debugger) {
}
// Remote inspector/edit.
void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {
void EditorDebuggerNode::_methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {
if (!singleton) {
return;
}
@ -729,7 +745,7 @@ void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const Stri
});
}
void EditorDebuggerNode::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
void EditorDebuggerNode::_properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
if (!singleton) {
return;
}

View file

@ -51,11 +51,8 @@ class EditorDebuggerNode : public MarginContainer {
public:
enum CameraOverride {
OVERRIDE_NONE,
OVERRIDE_2D,
OVERRIDE_3D_1, // 3D Viewport 1
OVERRIDE_3D_2, // 3D Viewport 2
OVERRIDE_3D_3, // 3D Viewport 3
OVERRIDE_3D_4 // 3D Viewport 4
OVERRIDE_INGAME,
OVERRIDE_EDITORS,
};
private:
@ -132,6 +129,7 @@ protected:
void _debugger_stopped(int p_id);
void _debugger_wants_stop(int p_id);
void _debugger_changed(int p_tab);
void _remote_tree_select_requested(ObjectID p_id, int p_debugger);
void _remote_tree_updated(int p_debugger);
void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _remote_object_updated(ObjectID p_id, int p_debugger);
@ -193,8 +191,8 @@ public:
// Remote inspector/edit.
void request_remote_tree();
static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
static void _methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
static void _properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
// LiveDebug
void set_live_debugging(bool p_enabled);

View file

@ -30,9 +30,7 @@
#include "editor_debugger_server.h"
#include "core/io/marshalls.h"
#include "core/io/tcp_server.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
@ -77,8 +75,8 @@ Error EditorDebuggerServerTCP::start(const String &p_uri) {
// Optionally override
if (!p_uri.is_empty() && p_uri != "tcp://") {
String scheme, path;
Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
String scheme, path, fragment;
Error err = p_uri.parse_url(scheme, bind_host, bind_port, path, fragment);
ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
}

View file

@ -30,7 +30,9 @@
#include "editor_debugger_tree.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene_tree_dock.h"
@ -124,6 +126,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
item_menu->clear();
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CreateNewSceneFrom")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CopyNodePath")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("Collapse")), TTR("Expand/Collapse Branch"), ITEM_MENU_EXPAND_COLLAPSE);
item_menu->set_position(get_screen_position() + get_local_mouse_position());
item_menu->reset_size();
item_menu->popup();
@ -144,23 +147,50 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
/// |-E
///
void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) {
set_hide_root(false);
updating_scene_tree = true;
const String last_path = get_selected_path();
const String filter = SceneTreeDock::get_singleton()->get_filter();
bool filter_changed = filter != last_filter;
TreeItem *select_item = nullptr;
bool hide_filtered_out_parents = EDITOR_GET("docks/scene_tree/hide_filtered_out_parents");
bool should_scroll = scrolling_to_item || filter != last_filter;
scrolling_to_item = false;
TreeItem *scroll_item = nullptr;
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
List<Pair<TreeItem *, int>> parents;
List<ParentItem> parents;
for (const SceneDebuggerTree::RemoteNode &node : p_tree->nodes) {
TreeItem *parent = nullptr;
Pair<TreeItem *, TreeItem *> move_from_to;
if (parents.size()) { // Find last parent.
Pair<TreeItem *, int> &p = parents.front()->get();
parent = p.first;
if (!(--p.second)) { // If no child left, remove it.
ParentItem &p = parents.front()->get();
parent = p.tree_item;
if (!(--p.child_count)) { // If no child left, remove it.
parents.pop_front();
if (hide_filtered_out_parents && !filter.is_subsequence_ofn(parent->get_text(0))) {
if (parent == get_root()) {
set_hide_root(true);
} else {
move_from_to.first = parent;
// Find the closest ancestor that matches the filter.
for (const ParentItem p2 : parents) {
move_from_to.second = p2.tree_item;
if (p2.matches_filter || move_from_to.second == get_root()) {
break;
}
}
if (!move_from_to.second) {
move_from_to.second = get_root();
}
}
}
}
}
// Add this node.
TreeItem *item = create_item(parent);
item->set_text(0, node.name);
@ -175,29 +205,41 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
}
item->set_metadata(0, node.id);
// Set current item as collapsed if necessary (root is never collapsed).
String current_path;
if (parent) {
current_path += (String)parent->get_meta("node_path");
// Set current item as collapsed if necessary (root is never collapsed).
if (!unfold_cache.has(node.id)) {
item->set_collapsed(true);
}
}
item->set_meta("node_path", current_path + "/" + item->get_text(0));
// Select previously selected node.
if (debugger_id == p_debugger) { // Can use remote id.
if (node.id == inspected_object_id) {
item->select(0);
if (filter_changed) {
if (selection_uncollapse_all) {
selection_uncollapse_all = false;
// Temporarily set to `false`, to allow caching the unfolds.
updating_scene_tree = false;
item->uncollapse_tree();
updating_scene_tree = true;
}
select_item = item;
if (should_scroll) {
scroll_item = item;
}
}
} else { // Must use path
if (last_path == _get_path(item)) {
updating_scene_tree = false; // Force emission of new selection.
item->select(0);
if (filter_changed) {
scroll_item = item;
}
updating_scene_tree = true;
} else if (last_path == (String)item->get_meta("node_path")) { // Must use path.
updating_scene_tree = false; // Force emission of new selection.
select_item = item;
if (should_scroll) {
scroll_item = item;
}
updating_scene_tree = true;
}
// Add buttons.
@ -229,7 +271,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
// Add in front of the parents stack if children are expected.
if (node.child_count) {
parents.push_front(Pair<TreeItem *, int>(item, node.child_count));
parents.push_front(ParentItem(item, node.child_count, filter.is_subsequence_ofn(item->get_text(0))));
} else {
// Apply filters.
while (parent) {
@ -237,34 +279,79 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
if (filter.is_subsequence_ofn(item->get_text(0))) {
break; // Filter matches, must survive.
}
parent->remove_child(item);
memdelete(item);
if (scroll_item == item) {
if (select_item == item || scroll_item == item) {
select_item = nullptr;
scroll_item = nullptr;
}
if (had_siblings) {
break; // Parent must survive.
}
item = parent;
parent = item->get_parent();
// Check if parent expects more children.
for (const Pair<TreeItem *, int> &pair : parents) {
if (pair.first == item) {
for (ParentItem &pair : parents) {
if (pair.tree_item == item) {
parent = nullptr;
break; // Might have more children.
}
}
}
}
// Move all children to the ancestor that matches the filter, if picked.
if (move_from_to.first) {
TreeItem *from = move_from_to.first;
TypedArray<TreeItem> children = from->get_children();
if (!children.is_empty()) {
for (Variant &c : children) {
TreeItem *ti = Object::cast_to<TreeItem>(c);
from->remove_child(ti);
move_from_to.second->add_child(ti);
}
from->get_parent()->remove_child(from);
memdelete(from);
if (select_item == from || scroll_item == from) {
select_item = nullptr;
scroll_item = nullptr;
}
}
}
}
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree.
if (select_item) {
select_item->select(0);
}
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree
if (scroll_item) {
callable_mp((Tree *)this, &Tree::scroll_to_item).call_deferred(scroll_item, false);
scroll_to_item(scroll_item, false);
}
last_filter = filter;
updating_scene_tree = false;
}
void EditorDebuggerTree::select_node(ObjectID p_id) {
// Manually select, as the tree control may be out-of-date for some reason (e.g. not shown yet).
selection_uncollapse_all = true;
inspected_object_id = uint64_t(p_id);
scrolling_to_item = true;
emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id);
if (!updating_scene_tree) {
// Request a tree refresh.
EditorDebuggerNode::get_singleton()->request_remote_tree();
}
// Set the value immediately, so no update flooding happens and causes a crash.
updating_scene_tree = true;
}
Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
if (get_button_id_at_position(p_point) != -1) {
return Variant();
@ -276,11 +363,14 @@ Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
}
String path = selected->get_text(0);
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
HBoxContainer *hb = memnew(HBoxContainer);
TextureRect *tf = memnew(TextureRect);
tf->set_texture(selected->get_icon(0));
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
tf->set_custom_minimum_size(Size2(icon_size, icon_size));
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
hb->add_child(tf);
Label *label = memnew(Label(path));
hb->add_child(label);
@ -306,22 +396,7 @@ String EditorDebuggerTree::get_selected_path() {
if (!get_selected()) {
return "";
}
return _get_path(get_selected());
}
String EditorDebuggerTree::_get_path(TreeItem *p_item) {
ERR_FAIL_NULL_V(p_item, "");
if (p_item->get_parent() == nullptr) {
return "/root";
}
String text = p_item->get_text(0);
TreeItem *cur = p_item->get_parent();
while (cur) {
text = cur->get_text(0) + "/" + text;
cur = cur->get_parent();
}
return "/" + text;
return get_selected()->get_meta("node_path");
}
void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
@ -350,7 +425,7 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
text = ".";
} else {
text = text.replace("/root/", "");
int slash = text.find("/");
int slash = text.find_char('/');
if (slash < 0) {
text = ".";
} else {
@ -359,6 +434,21 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
}
DisplayServer::get_singleton()->clipboard_set(text);
} break;
case ITEM_MENU_EXPAND_COLLAPSE: {
TreeItem *s_item = get_selected();
if (!s_item) {
s_item = get_root();
if (!s_item) {
break;
}
}
bool collapsed = s_item->is_any_collapsed();
s_item->set_collapsed_recursive(!collapsed);
ensure_cursor_is_visible();
}
}
}

View file

@ -40,20 +40,34 @@ class EditorDebuggerTree : public Tree {
GDCLASS(EditorDebuggerTree, Tree);
private:
struct ParentItem {
TreeItem *tree_item;
int child_count;
bool matches_filter;
ParentItem(TreeItem *p_tree_item = nullptr, int p_child_count = 0, bool p_matches_filter = false) {
tree_item = p_tree_item;
child_count = p_child_count;
matches_filter = p_matches_filter;
}
};
enum ItemMenu {
ITEM_MENU_SAVE_REMOTE_NODE,
ITEM_MENU_COPY_NODE_PATH,
ITEM_MENU_EXPAND_COLLAPSE,
};
ObjectID inspected_object_id;
int debugger_id = 0;
bool updating_scene_tree = false;
bool scrolling_to_item = false;
bool selection_uncollapse_all = false;
HashSet<ObjectID> unfold_cache;
PopupMenu *item_menu = nullptr;
EditorFileDialog *file_dialog = nullptr;
String last_filter;
String _get_path(TreeItem *p_item);
void _scene_tree_folded(Object *p_obj);
void _scene_tree_selected();
void _scene_tree_rmb_selected(const Vector2 &p_position, MouseButton p_button);
@ -77,6 +91,7 @@ public:
ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger.
void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger);
void select_node(ObjectID p_id);
EditorDebuggerTree();
};

View file

@ -0,0 +1,145 @@
/**************************************************************************/
/* editor_expression_evaluator.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "editor_expression_evaluator.h"
#include "editor/debugger/editor_debugger_inspector.h"
#include "editor/debugger/script_editor_debugger.h"
#include "scene/gui/button.h"
#include "scene/gui/check_box.h"
void EditorExpressionEvaluator::on_start() {
expression_input->set_editable(false);
evaluate_btn->set_disabled(true);
if (clear_on_run_checkbox->is_pressed()) {
inspector->clear_stack_variables();
}
}
void EditorExpressionEvaluator::set_editor_debugger(ScriptEditorDebugger *p_editor_debugger) {
editor_debugger = p_editor_debugger;
}
void EditorExpressionEvaluator::add_value(const Array &p_array) {
inspector->add_stack_variable(p_array, 0);
inspector->set_v_scroll(0);
inspector->set_h_scroll(0);
}
void EditorExpressionEvaluator::_evaluate() {
const String &expression = expression_input->get_text();
if (expression.is_empty()) {
return;
}
if (!editor_debugger->is_session_active()) {
return;
}
editor_debugger->request_remote_evaluate(expression, editor_debugger->get_stack_script_frame());
expression_input->clear();
}
void EditorExpressionEvaluator::_clear() {
inspector->clear_stack_variables();
}
void EditorExpressionEvaluator::_remote_object_selected(ObjectID p_id) {
editor_debugger->emit_signal(SNAME("remote_object_requested"), p_id);
}
void EditorExpressionEvaluator::_on_expression_input_changed(const String &p_expression) {
evaluate_btn->set_disabled(p_expression.is_empty());
}
void EditorExpressionEvaluator::_on_debugger_breaked(bool p_breaked, bool p_can_debug) {
expression_input->set_editable(p_breaked);
evaluate_btn->set_disabled(!p_breaked);
}
void EditorExpressionEvaluator::_on_debugger_clear_execution(Ref<Script> p_stack_script) {
expression_input->set_editable(false);
evaluate_btn->set_disabled(true);
}
void EditorExpressionEvaluator::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
EditorDebuggerNode::get_singleton()->connect("breaked", callable_mp(this, &EditorExpressionEvaluator::_on_debugger_breaked));
EditorDebuggerNode::get_singleton()->connect("clear_execution", callable_mp(this, &EditorExpressionEvaluator::_on_debugger_clear_execution));
} break;
}
}
EditorExpressionEvaluator::EditorExpressionEvaluator() {
set_h_size_flags(SIZE_EXPAND_FILL);
HBoxContainer *hb = memnew(HBoxContainer);
add_child(hb);
expression_input = memnew(LineEdit);
expression_input->set_h_size_flags(Control::SIZE_EXPAND_FILL);
expression_input->set_placeholder(TTR("Expression to evaluate"));
expression_input->set_clear_button_enabled(true);
expression_input->connect(SceneStringName(text_submitted), callable_mp(this, &EditorExpressionEvaluator::_evaluate).unbind(1));
expression_input->connect(SceneStringName(text_changed), callable_mp(this, &EditorExpressionEvaluator::_on_expression_input_changed));
hb->add_child(expression_input);
clear_on_run_checkbox = memnew(CheckBox);
clear_on_run_checkbox->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
clear_on_run_checkbox->set_text(TTR("Clear on Run"));
clear_on_run_checkbox->set_pressed(true);
hb->add_child(clear_on_run_checkbox);
evaluate_btn = memnew(Button);
evaluate_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
evaluate_btn->set_text(TTR("Evaluate"));
evaluate_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorExpressionEvaluator::_evaluate));
hb->add_child(evaluate_btn);
clear_btn = memnew(Button);
clear_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
clear_btn->set_text(TTR("Clear"));
clear_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorExpressionEvaluator::_clear));
hb->add_child(clear_btn);
inspector = memnew(EditorDebuggerInspector);
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);
inspector->set_read_only(true);
inspector->connect("object_selected", callable_mp(this, &EditorExpressionEvaluator::_remote_object_selected));
inspector->set_use_filter(true);
add_child(inspector);
expression_input->set_editable(false);
evaluate_btn->set_disabled(true);
}

View file

@ -0,0 +1,77 @@
/**************************************************************************/
/* editor_expression_evaluator.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_EXPRESSION_EVALUATOR_H
#define EDITOR_EXPRESSION_EVALUATOR_H
#include "scene/gui/box_container.h"
class Button;
class CheckBox;
class EditorDebuggerInspector;
class LineEdit;
class RemoteDebuggerPeer;
class ScriptEditorDebugger;
class EditorExpressionEvaluator : public VBoxContainer {
GDCLASS(EditorExpressionEvaluator, VBoxContainer)
private:
Ref<RemoteDebuggerPeer> peer;
LineEdit *expression_input = nullptr;
CheckBox *clear_on_run_checkbox = nullptr;
Button *evaluate_btn = nullptr;
Button *clear_btn = nullptr;
EditorDebuggerInspector *inspector = nullptr;
void _evaluate();
void _clear();
void _remote_object_selected(ObjectID p_id);
void _on_expression_input_changed(const String &p_expression);
void _on_debugger_breaked(bool p_breaked, bool p_can_debug);
void _on_debugger_clear_execution(Ref<Script> p_stack_script);
protected:
ScriptEditorDebugger *editor_debugger = nullptr;
void _notification(int p_what);
public:
void on_start();
void set_editor_debugger(ScriptEditorDebugger *p_editor_debugger);
void add_value(const Array &p_array);
EditorExpressionEvaluator();
};
#endif // EDITOR_EXPRESSION_EVALUATOR_H

View file

@ -31,7 +31,6 @@
#include "editor_file_server.h"
#include "../editor_settings.h"
#include "core/io/marshalls.h"
#include "editor/editor_node.h"
#include "editor/export/editor_export_platform.h"
@ -271,9 +270,6 @@ void EditorFileServer::stop() {
EditorFileServer::EditorFileServer() {
server.instantiate();
EDITOR_DEF("filesystem/file_server/port", 6010);
EDITOR_DEF("filesystem/file_server/password", "");
}
EditorFileServer::~EditorFileServer() {

View file

@ -31,9 +31,7 @@
#ifndef EDITOR_FILE_SERVER_H
#define EDITOR_FILE_SERVER_H
#include "core/io/packet_peer.h"
#include "core/io/tcp_server.h"
#include "core/object/class_db.h"
#include "core/os/thread.h"
#include "editor/editor_file_system.h"

View file

@ -81,6 +81,9 @@ void EditorPerformanceProfiler::Monitor::reset() {
String EditorPerformanceProfiler::_create_label(float p_value, Performance::MonitorType p_type) {
switch (p_type) {
case Performance::MONITOR_TYPE_QUANTITY: {
return TS->format_number(itos(p_value));
}
case Performance::MONITOR_TYPE_MEMORY: {
return String::humanize_size(p_value);
}
@ -393,17 +396,23 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() {
set_split_offset(340 * EDSCALE);
monitor_tree = memnew(Tree);
monitor_tree->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
monitor_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
monitor_tree->set_columns(2);
monitor_tree->set_column_title(0, TTR("Monitor"));
monitor_tree->set_column_expand(0, true);
monitor_tree->set_column_title(1, TTR("Value"));
monitor_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);
monitor_tree->set_column_expand(1, false);
monitor_tree->set_column_titles_visible(true);
monitor_tree->connect("item_edited", callable_mp(this, &EditorPerformanceProfiler::_monitor_select));
monitor_tree->create_item();
monitor_tree->set_hide_root(true);
monitor_tree->set_theme_type_variation("TreeSecondary");
add_child(monitor_tree);
monitor_draw = memnew(Control);
monitor_draw->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
monitor_draw->set_clip_contents(true);
monitor_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPerformanceProfiler::_monitor_draw));
monitor_draw->connect(SceneStringName(gui_input), callable_mp(this, &EditorPerformanceProfiler::_marker_input));

View file

@ -32,7 +32,6 @@
#define EDITOR_PERFORMANCE_PROFILER_H
#include "core/templates/hash_map.h"
#include "core/templates/rb_map.h"
#include "main/performance.h"
#include "scene/gui/control.h"
#include "scene/gui/label.h"

View file

@ -30,11 +30,13 @@
#include "editor_profiler.h"
#include "core/os/os.h"
#include "core/io/image.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/check_box.h"
#include "scene/gui/flow_container.h"
#include "scene/resources/image_texture.h"
void EditorProfiler::_make_metric_ptrs(Metric &m) {
@ -150,6 +152,11 @@ Color EditorProfiler::_get_color_from_signature(const StringName &p_signature) c
return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);
}
int EditorProfiler::_get_zoom_left_border() const {
const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom);
return CLAMP(zoom_center - max_profiles_shown / 2, 0, frame_metrics.size() - max_profiles_shown);
}
void EditorProfiler::_item_edited() {
if (updating_frame) {
return;
@ -177,8 +184,8 @@ void EditorProfiler::_item_edited() {
}
void EditorProfiler::_update_plot() {
const int w = graph->get_size().width;
const int h = graph->get_size().height;
const int w = MAX(1, graph->get_size().width); // Clamp to 1 to prevent from crashing when profiler is autostarted.
const int h = MAX(1, graph->get_size().height);
bool reset_texture = false;
const int desired_len = w * h * 4;
@ -235,12 +242,17 @@ void EditorProfiler::_update_plot() {
HashMap<StringName, int> prev_plots;
for (int i = 0; i < total_metrics * w / frame_metrics.size() - 1; i++) {
const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom);
const int left_border = _get_zoom_left_border();
const int profiles_drawn = CLAMP(total_metrics - left_border, 0, max_profiles_shown);
const int pixel_cols = (profiles_drawn * w) / max_profiles_shown - 1;
for (int i = 0; i < pixel_cols; i++) {
for (int j = 0; j < h * 4; j++) {
column[j] = 0;
}
int current = i * frame_metrics.size() / w;
int current = (i * max_profiles_shown / w) + left_border;
for (const StringName &E : plot_sigs) {
const Metric &m = _get_frame_metric(current);
@ -388,10 +400,10 @@ void EditorProfiler::_update_frame() {
void EditorProfiler::_update_button_text() {
if (activate->is_pressed()) {
activate->set_icon(get_editor_theme_icon(SNAME("Stop")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
activate->set_text(TTR("Stop"));
} else {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
activate->set_text(TTR("Start"));
}
}
@ -416,14 +428,19 @@ void EditorProfiler::_internal_profiles_pressed() {
_combo_changed(0);
}
void EditorProfiler::_autostart_toggled(bool p_toggled_on) {
EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_profiler", p_toggled_on);
EditorRunBar::get_singleton()->update_profiler_autostart_indicator();
}
void EditorProfiler::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear")));
theme_cache.seek_line_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
theme_cache.seek_line_color.a = 0.8;
@ -443,10 +460,12 @@ void EditorProfiler::_graph_tex_draw() {
}
if (seeking) {
int frame = cursor_metric_edit->get_value() - _get_frame_metric(0).frame_number;
int cur_x = (2 * frame + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
frame = frame - _get_zoom_left_border() + 1;
int cur_x = (frame * graph->get_size().width * Math::exp(graph_zoom)) / frame_metrics.size();
cur_x = CLAMP(cur_x, 0, graph->get_size().width);
graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_color);
}
if (hover_metric > -1 && hover_metric < total_metrics) {
if (hover_metric > -1) {
int cur_x = (2 * hover_metric + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_hover_color);
}
@ -474,22 +493,17 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouse> me = p_ev;
Ref<InputEventMouseButton> mb = p_ev;
Ref<InputEventMouseMotion> mm = p_ev;
MouseButton button_idx = mb.is_valid() ? mb->get_button_index() : MouseButton();
if (
(mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) ||
(mb.is_valid() && button_idx == MouseButton::LEFT && mb->is_pressed()) ||
(mm.is_valid())) {
int x = me->get_position().x - 1;
hover_metric = x * frame_metrics.size() / graph->get_size().width;
x = x * frame_metrics.size() / graph->get_size().width;
hover_metric = x;
if (x < 0) {
x = 0;
}
if (x >= frame_metrics.size()) {
x = frame_metrics.size() - 1;
}
x = x / Math::exp(graph_zoom) + _get_zoom_left_border();
x = CLAMP(x, 0, frame_metrics.size() - 1);
if (mb.is_valid() || (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
updating_frame = true;
@ -512,9 +526,34 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
frame_delay->start();
}
}
graph->queue_redraw();
}
if (graph_zoom > 0 && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::MIDDLE) || mm->get_button_mask().has_flag(MouseButtonMask::RIGHT))) {
// Panning.
const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom);
pan_accumulator += (float)mm->get_relative().x * max_profiles_shown / graph->get_size().width;
if (Math::abs(pan_accumulator) > 1) {
zoom_center = CLAMP(zoom_center - (int)pan_accumulator, max_profiles_shown / 2, frame_metrics.size() - max_profiles_shown / 2);
pan_accumulator -= (int)pan_accumulator;
_update_plot();
}
}
if (button_idx == MouseButton::WHEEL_DOWN) {
// Zooming.
graph_zoom = MAX(-0.05 + graph_zoom, 0);
_update_plot();
} else if (button_idx == MouseButton::WHEEL_UP) {
if (graph_zoom == 0) {
zoom_center = me->get_position().x;
zoom_center = zoom_center * frame_metrics.size() / graph->get_size().width;
}
graph_zoom = MIN(0.05 + graph_zoom, 2);
_update_plot();
}
graph->queue_redraw();
}
void EditorProfiler::disable_seeking() {
@ -539,9 +578,10 @@ void EditorProfiler::set_enabled(bool p_enable, bool p_clear) {
}
}
void EditorProfiler::set_pressed(bool p_pressed) {
void EditorProfiler::set_profiling(bool p_pressed) {
activate->set_pressed(p_pressed);
_update_button_text();
emit_signal(SNAME("enable_profiling"), activate->is_pressed());
}
bool EditorProfiler::is_profiling() {
@ -619,21 +659,39 @@ Vector<Vector<String>> EditorProfiler::get_data_as_csv() const {
EditorProfiler::EditorProfiler() {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);
add_child(hb);
FlowContainer *container = memnew(FlowContainer);
container->set_h_size_flags(SIZE_EXPAND_FILL);
container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);
container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);
hb->add_child(container);
activate = memnew(Button);
activate->set_toggle_mode(true);
activate->set_disabled(true);
activate->set_text(TTR("Start"));
activate->connect(SceneStringName(pressed), callable_mp(this, &EditorProfiler::_activate_pressed));
hb->add_child(activate);
container->add_child(activate);
clear_button = memnew(Button);
clear_button->set_text(TTR("Clear"));
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorProfiler::_clear_pressed));
clear_button->set_disabled(true);
hb->add_child(clear_button);
container->add_child(clear_button);
hb->add_child(memnew(Label(TTR("Measure:"))));
CheckBox *autostart_checkbox = memnew(CheckBox);
autostart_checkbox->set_text(TTR("Autostart"));
autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false));
autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorProfiler::_autostart_toggled));
container->add_child(autostart_checkbox);
HBoxContainer *hb_measure = memnew(HBoxContainer);
hb_measure->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
container->add_child(hb_measure);
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
display_mode->add_item(TTR("Frame Time (ms)"));
@ -642,9 +700,13 @@ EditorProfiler::EditorProfiler() {
display_mode->add_item(TTR("Physics Frame %"));
display_mode->connect(SceneStringName(item_selected), callable_mp(this, &EditorProfiler::_combo_changed));
hb->add_child(display_mode);
hb_measure->add_child(display_mode);
hb->add_child(memnew(Label(TTR("Time:"))));
HBoxContainer *hb_time = memnew(HBoxContainer);
hb_time->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
container->add_child(hb_time);
hb_time->add_child(memnew(Label(TTR("Time:"))));
display_time = memnew(OptionButton);
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, including the time spent in other functions called by that function.
@ -653,28 +715,28 @@ EditorProfiler::EditorProfiler() {
display_time->add_item(TTR("Self"));
display_time->set_tooltip_text(TTR("Inclusive: Includes time from other functions called by this function.\nUse this to spot bottlenecks.\n\nSelf: Only count the time spent in the function itself, not in other functions called by that function.\nUse this to find individual functions to optimize."));
display_time->connect(SceneStringName(item_selected), callable_mp(this, &EditorProfiler::_combo_changed));
hb->add_child(display_time);
hb_time->add_child(display_time);
display_internal_profiles = memnew(CheckButton(TTR("Display internal functions")));
display_internal_profiles->set_visible(EDITOR_GET("debugger/profile_native_calls"));
display_internal_profiles->set_pressed(false);
display_internal_profiles->connect(SceneStringName(pressed), callable_mp(this, &EditorProfiler::_internal_profiles_pressed));
hb->add_child(display_internal_profiles);
container->add_child(display_internal_profiles);
hb->add_spacer();
HBoxContainer *hb_frame = memnew(HBoxContainer);
hb_frame->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
hb_frame->set_v_size_flags(SIZE_SHRINK_BEGIN);
hb->add_child(hb_frame);
hb->add_child(memnew(Label(TTR("Frame #:"))));
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
cursor_metric_edit = memnew(SpinBox);
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
cursor_metric_edit->set_value(0);
cursor_metric_edit->set_editable(false);
hb->add_child(cursor_metric_edit);
hb_frame->add_child(cursor_metric_edit);
cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorProfiler::_cursor_metric_changed));
hb->add_theme_constant_override("separation", 8 * EDSCALE);
h_split = memnew(HSplitContainer);
add_child(h_split);
h_split->set_v_size_flags(SIZE_EXPAND_FILL);
@ -699,9 +761,11 @@ EditorProfiler::EditorProfiler() {
variables->set_column_expand(2, false);
variables->set_column_clip_content(2, true);
variables->set_column_custom_minimum_width(2, 50 * EDSCALE);
variables->set_theme_type_variation("TreeSecondary");
variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited));
graph = memnew(TextureRect);
graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0));
graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
graph->set_mouse_filter(MOUSE_FILTER_STOP);
graph->connect(SceneStringName(draw), callable_mp(this, &EditorProfiler::_graph_tex_draw));

View file

@ -104,6 +104,11 @@ private:
TextureRect *graph = nullptr;
Ref<ImageTexture> graph_texture;
Vector<uint8_t> graph_image;
float graph_zoom = 0.0f;
float pan_accumulator = 0.0f;
int zoom_center = -1;
Tree *variables = nullptr;
HSplitContainer *h_split = nullptr;
@ -138,6 +143,7 @@ private:
void _activate_pressed();
void _clear_pressed();
void _autostart_toggled(bool p_toggled_on);
void _internal_profiles_pressed();
@ -154,6 +160,7 @@ private:
void _graph_tex_input(const Ref<InputEvent> &p_ev);
Color _get_color_from_signature(const StringName &p_signature) const;
int _get_zoom_left_border() const;
void _cursor_metric_changed(double);
@ -168,7 +175,7 @@ protected:
public:
void add_frame_metric(const Metric &p_metric, bool p_final = false);
void set_enabled(bool p_enable, bool p_clear = true);
void set_pressed(bool p_pressed);
void set_profiling(bool p_pressed);
bool is_profiling();
bool is_seeking() { return seeking; }
void disable_seeking();

View file

@ -30,12 +30,20 @@
#include "editor_visual_profiler.h"
#include "core/os/os.h"
#include "core/io/image.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/flow_container.h"
#include "scene/resources/image_texture.h"
void EditorVisualProfiler::set_hardware_info(const String &p_cpu_name, const String &p_gpu_name) {
cpu_name = p_cpu_name;
gpu_name = p_gpu_name;
queue_redraw();
}
void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) {
++last_metric;
if (last_metric >= frame_metrics.size()) {
@ -103,6 +111,8 @@ void EditorVisualProfiler::clear() {
variables->clear();
//activate->set_pressed(false);
graph_limit = 1000.0f / CLAMP(int(EDITOR_GET("debugger/profiler_target_fps")), 1, 1000);
updating_frame = true;
cursor_metric_edit->set_min(0);
cursor_metric_edit->set_max(0);
@ -146,8 +156,8 @@ void EditorVisualProfiler::_item_selected() {
}
void EditorVisualProfiler::_update_plot() {
const int w = graph->get_size().width;
const int h = graph->get_size().height;
const int w = graph->get_size().width + 1; // `+1` is to prevent from crashing when visual profiler is auto started.
const int h = graph->get_size().height + 1;
bool reset_texture = false;
@ -408,12 +418,12 @@ void EditorVisualProfiler::_update_frame(bool p_focus_selected) {
void EditorVisualProfiler::_activate_pressed() {
if (activate->is_pressed()) {
activate->set_icon(get_editor_theme_icon(SNAME("Stop")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
activate->set_text(TTR("Stop"));
_clear_pressed(); //always clear on start
clear_button->set_disabled(false);
} else {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
activate->set_text(TTR("Start"));
}
emit_signal(SNAME("enable_profiling"), activate->is_pressed());
@ -425,18 +435,19 @@ void EditorVisualProfiler::_clear_pressed() {
_update_plot();
}
void EditorVisualProfiler::_autostart_toggled(bool p_toggled_on) {
EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_visual_profiler", p_toggled_on);
EditorRunBar::get_singleton()->update_profiler_autostart_indicator();
}
void EditorVisualProfiler::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
if (is_layout_rtl()) {
activate->set_icon(get_editor_theme_icon(SNAME("PlayBackwards")));
} else {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
}
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear")));
} break;
}
}
@ -486,8 +497,8 @@ void EditorVisualProfiler::_graph_tex_draw() {
graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));
}
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1));
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1));
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU: " + cpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU: " + gpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));
}
void EditorVisualProfiler::_graph_tex_mouse_exit() {
@ -654,10 +665,10 @@ void EditorVisualProfiler::_bind_methods() {
void EditorVisualProfiler::_update_button_text() {
if (activate->is_pressed()) {
activate->set_icon(get_editor_theme_icon(SNAME("Stop")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
activate->set_text(TTR("Stop"));
} else {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
activate->set_text(TTR("Start"));
}
}
@ -666,9 +677,10 @@ void EditorVisualProfiler::set_enabled(bool p_enable) {
activate->set_disabled(!p_enable);
}
void EditorVisualProfiler::set_pressed(bool p_pressed) {
activate->set_pressed(p_pressed);
void EditorVisualProfiler::set_profiling(bool p_profiling) {
activate->set_pressed(p_profiling);
_update_button_text();
emit_signal(SNAME("enable_profiling"), activate->is_pressed());
}
bool EditorVisualProfiler::is_profiling() {
@ -731,49 +743,68 @@ Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const {
EditorVisualProfiler::EditorVisualProfiler() {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);
add_child(hb);
FlowContainer *container = memnew(FlowContainer);
container->set_h_size_flags(SIZE_EXPAND_FILL);
container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);
container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);
hb->add_child(container);
activate = memnew(Button);
activate->set_toggle_mode(true);
activate->set_disabled(true);
activate->set_text(TTR("Start"));
activate->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_activate_pressed));
hb->add_child(activate);
container->add_child(activate);
clear_button = memnew(Button);
clear_button->set_text(TTR("Clear"));
clear_button->set_disabled(true);
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_clear_pressed));
hb->add_child(clear_button);
container->add_child(clear_button);
hb->add_child(memnew(Label(TTR("Measure:"))));
CheckBox *autostart_checkbox = memnew(CheckBox);
autostart_checkbox->set_text(TTR("Autostart"));
autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false));
autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorVisualProfiler::_autostart_toggled));
container->add_child(autostart_checkbox);
HBoxContainer *hb_measure = memnew(HBoxContainer);
hb_measure->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
container->add_child(hb_measure);
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
display_mode->add_item(TTR("Frame Time (ms)"));
display_mode->add_item(TTR("Frame %"));
display_mode->connect(SceneStringName(item_selected), callable_mp(this, &EditorVisualProfiler::_combo_changed));
hb->add_child(display_mode);
hb_measure->add_child(display_mode);
frame_relative = memnew(CheckBox(TTR("Fit to Frame")));
frame_relative->set_pressed(true);
hb->add_child(frame_relative);
container->add_child(frame_relative);
frame_relative->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_update_plot));
linked = memnew(CheckBox(TTR("Linked")));
linked->set_pressed(true);
hb->add_child(linked);
container->add_child(linked);
linked->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_update_plot));
hb->add_spacer();
HBoxContainer *hb_frame = memnew(HBoxContainer);
hb_frame->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
hb_frame->set_v_size_flags(SIZE_SHRINK_BEGIN);
hb->add_child(hb_frame);
hb->add_child(memnew(Label(TTR("Frame #:"))));
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
cursor_metric_edit = memnew(SpinBox);
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
hb->add_child(cursor_metric_edit);
hb_frame->add_child(cursor_metric_edit);
cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorVisualProfiler::_cursor_metric_changed));
hb->add_theme_constant_override("separation", 8 * EDSCALE);
h_split = memnew(HSplitContainer);
add_child(h_split);
h_split->set_v_size_flags(SIZE_EXPAND_FILL);
@ -797,9 +828,11 @@ EditorVisualProfiler::EditorVisualProfiler() {
variables->set_column_expand(2, false);
variables->set_column_clip_content(2, true);
variables->set_column_custom_minimum_width(2, 75 * EDSCALE);
variables->set_theme_type_variation("TreeSecondary");
variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected));
graph = memnew(TextureRect);
graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0));
graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
graph->set_mouse_filter(MOUSE_FILTER_STOP);
graph->connect(SceneStringName(draw), callable_mp(this, &EditorVisualProfiler::_graph_tex_draw));
@ -812,6 +845,8 @@ EditorVisualProfiler::EditorVisualProfiler() {
int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000);
frame_metrics.resize(metric_size);
graph_limit = 1000.0f / CLAMP(int(EDITOR_GET("debugger/profiler_target_fps")), 1, 1000);
frame_delay = memnew(Timer);
frame_delay->set_wait_time(0.1);
frame_delay->set_one_shot(true);

View file

@ -98,6 +98,9 @@ private:
float graph_limit = 1000.0f / 60;
String cpu_name;
String gpu_name;
bool seeking = false;
Timer *frame_delay = nullptr;
@ -109,6 +112,7 @@ private:
void _activate_pressed();
void _clear_pressed();
void _autostart_toggled(bool p_toggled_on);
String _get_time_as_text(float p_time);
@ -135,9 +139,10 @@ protected:
static void _bind_methods();
public:
void set_hardware_info(const String &p_cpu_name, const String &p_gpu_name);
void add_frame_metric(const Metric &p_metric);
void set_enabled(bool p_enable);
void set_pressed(bool p_pressed);
void set_profiling(bool p_profiling);
bool is_profiling();
bool is_seeking() { return seeking; }
void disable_seeking();

View file

@ -37,6 +37,7 @@
#include "core/string/ustring.h"
#include "core/version.h"
#include "editor/debugger/debug_adapter/debug_adapter_protocol.h"
#include "editor/debugger/editor_expression_evaluator.h"
#include "editor/debugger/editor_performance_profiler.h"
#include "editor/debugger/editor_profiler.h"
#include "editor/debugger/editor_visual_profiler.h"
@ -60,13 +61,10 @@
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/separator.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/texture_button.h"
#include "scene/gui/tree.h"
#include "scene/resources/packed_scene.h"
#include "servers/debugger/servers_debugger.h"
#include "servers/display_server.h"
@ -94,9 +92,9 @@ void ScriptEditorDebugger::debug_copy() {
void ScriptEditorDebugger::debug_skip_breakpoints() {
skip_breakpoints_value = !skip_breakpoints_value;
if (skip_breakpoints_value) {
skip_breakpoints->set_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));
skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));
} else {
skip_breakpoints->set_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));
skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));
}
Array msg;
@ -252,6 +250,13 @@ const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {
return scene_tree;
}
void ScriptEditorDebugger::request_remote_evaluate(const String &p_expression, int p_stack_frame) {
Array msg;
msg.push_back(p_expression);
msg.push_back(p_stack_frame);
_put_msg("evaluate", msg);
}
void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value) {
Array msg;
msg.push_back(p_obj_id);
@ -307,7 +312,7 @@ void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) {
ThreadDebugged &td = threads_debugged[p_thread_id];
_set_reason_text(td.error, MESSAGE_ERROR);
emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump);
if (!td.error.is_empty()) {
if (!td.error.is_empty() && EDITOR_GET("debugger/auto_switch_to_stack_trace")) {
tabs->set_current_tab(0);
}
inspector->clear_cache(); // Take a chance to force remote objects update.
@ -496,7 +501,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
} break;
}
EditorNode::get_log()->add_message(output_strings[i], msg_type);
emit_signal(SNAME("output"), output_strings[i], msg_type);
emit_signal(SceneStringName(output), output_strings[i], msg_type);
}
} else if (p_msg == "performance:profile_frame") {
Vector<float> frame_data;
@ -505,6 +510,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
frame_data.write[i] = p_data[i];
}
performance_profiler->add_profile_frame(frame_data);
} else if (p_msg == "visual:hardware_info") {
const String cpu_name = p_data[0];
const String gpu_name = p_data[1];
visual_profiler->set_hardware_info(cpu_name, gpu_name);
} else if (p_msg == "visual:profile_frame") {
ServersDebugger::VisualProfilerFrame frame;
frame.deserialize(p_data);
@ -534,7 +543,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
time_vals.push_back(oe.sec);
time_vals.push_back(oe.msec);
bool e;
String time = String("%d:%02d:%02d:%04d").sprintf(time_vals, &e);
String time = String("%d:%02d:%02d:%03d").sprintf(time_vals, &e);
// Rest of the error data.
bool source_is_project_file = oe.source_file.begins_with("res://");
@ -798,6 +807,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
} else if (p_msg == "request_quit") {
emit_signal(SNAME("stop_requested"));
_stop_and_notify();
} else if (p_msg == "remote_node_clicked") {
if (!p_data.is_empty()) {
emit_signal(SNAME("remote_tree_select_requested"), p_data[0]);
}
} else if (p_msg == "performance:profile_names") {
Vector<StringName> monitors;
monitors.resize(p_data.size());
@ -811,13 +824,18 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
if (EditorFileSystem::get_singleton()) {
EditorFileSystem::get_singleton()->update_file(p_data[0]);
}
} else if (p_msg == "evaluation_return") {
expression_evaluator->add_value(p_data);
} else if (p_msg == "window:title") {
ERR_FAIL_COND(p_data.size() != 1);
emit_signal(SNAME("remote_window_title_changed"), p_data[0]);
} else {
int colon_index = p_msg.find_char(':');
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);
if (!parsed) {
WARN_PRINT("unknown message " + p_msg);
WARN_PRINT("Unknown message: " + p_msg);
}
}
}
@ -854,19 +872,20 @@ void ScriptEditorDebugger::_notification(int p_what) {
error_tree->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_error_selected));
error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));
breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));
[[fallthrough]];
}
connect("started", callable_mp(expression_evaluator, &EditorExpressionEvaluator::on_start));
} break;
case NOTIFICATION_THEME_CHANGED: {
tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));
skip_breakpoints->set_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));
copy->set_icon(get_editor_theme_icon(SNAME("ActionCopy")));
step->set_icon(get_editor_theme_icon(SNAME("DebugStep")));
next->set_icon(get_editor_theme_icon(SNAME("DebugNext")));
dobreak->set_icon(get_editor_theme_icon(SNAME("Pause")));
docontinue->set_icon(get_editor_theme_icon(SNAME("DebugContinue")));
vmem_refresh->set_icon(get_editor_theme_icon(SNAME("Reload")));
vmem_export->set_icon(get_editor_theme_icon(SNAME("Save")));
skip_breakpoints->set_button_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));
copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));
step->set_button_icon(get_editor_theme_icon(SNAME("DebugStep")));
next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext")));
dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue")));
vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));
search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
@ -894,37 +913,42 @@ void ScriptEditorDebugger::_notification(int p_what) {
if (is_session_active()) {
peer->poll();
if (camera_override == CameraOverride::OVERRIDE_2D) {
Dictionary state = CanvasItemEditor::get_singleton()->get_state();
float zoom = state["zoom"];
Point2 offset = state["ofs"];
Transform2D transform;
if (camera_override == CameraOverride::OVERRIDE_EDITORS) {
// CanvasItem Editor
{
Dictionary state = CanvasItemEditor::get_singleton()->get_state();
float zoom = state["zoom"];
Point2 offset = state["ofs"];
Transform2D transform;
transform.scale_basis(Size2(zoom, zoom));
transform.columns[2] = -offset * zoom;
transform.scale_basis(Size2(zoom, zoom));
transform.columns[2] = -offset * zoom;
Array msg;
msg.push_back(transform);
_put_msg("scene:override_camera_2D:transform", msg);
} else if (camera_override >= CameraOverride::OVERRIDE_3D_1) {
int viewport_idx = camera_override - CameraOverride::OVERRIDE_3D_1;
Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(viewport_idx);
Camera3D *const cam = viewport->get_camera_3d();
Array msg;
msg.push_back(cam->get_camera_transform());
if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
msg.push_back(false);
msg.push_back(cam->get_size());
} else {
msg.push_back(true);
msg.push_back(cam->get_fov());
Array msg;
msg.push_back(transform);
_put_msg("scene:transform_camera_2d", msg);
}
// Node3D Editor
{
Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport();
const Camera3D *cam = viewport->get_camera_3d();
Array msg;
msg.push_back(cam->get_camera_transform());
if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
msg.push_back(false);
msg.push_back(cam->get_size());
} else {
msg.push_back(true);
msg.push_back(cam->get_fov());
}
msg.push_back(cam->get_near());
msg.push_back(cam->get_far());
_put_msg("scene:transform_camera_3d", msg);
}
msg.push_back(cam->get_near());
msg.push_back(cam->get_far());
_put_msg("scene:override_camera_3D:transform", msg);
}
if (is_breaked() && can_request_idle_draw) {
_put_msg("servers:draw", Array());
can_request_idle_draw = false;
@ -1012,6 +1036,17 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
_update_buttons_state();
emit_signal(SNAME("started"));
Array quit_keys = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
_put_msg("scene:setup_scene", quit_keys);
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {
profiler->set_profiling(true);
}
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {
visual_profiler->set_profiling(true);
}
}
void ScriptEditorDebugger::_update_buttons_state() {
@ -1031,6 +1066,7 @@ void ScriptEditorDebugger::_update_buttons_state() {
for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {
threadss.push_back(&I.value);
}
threads->set_disabled(threadss.is_empty());
threadss.sort_custom<ThreadSort>();
threads->clear();
@ -1076,10 +1112,10 @@ void ScriptEditorDebugger::stop() {
profiler_signature.clear();
profiler->set_enabled(false, false);
profiler->set_pressed(false);
profiler->set_profiling(false);
visual_profiler->set_enabled(false);
visual_profiler->set_pressed(false);
visual_profiler->set_profiling(false);
inspector->edit(nullptr);
_update_buttons_state();
@ -1142,6 +1178,12 @@ String ScriptEditorDebugger::get_var_value(const String &p_var) const {
return inspector->get_stack_variable(p_var);
}
void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_resources) {
Array msg;
msg.push_back(p_resources);
_put_msg("scene:reload_cached_files", msg);
}
int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
const int *r = node_path_cache.getptr(p_path);
if (r) {
@ -1450,23 +1492,10 @@ CameraOverride ScriptEditorDebugger::get_camera_override() const {
}
void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
if (p_override == CameraOverride::OVERRIDE_2D && camera_override != CameraOverride::OVERRIDE_2D) {
Array msg;
msg.push_back(true);
_put_msg("scene:override_camera_2D:set", msg);
} else if (p_override != CameraOverride::OVERRIDE_2D && camera_override == CameraOverride::OVERRIDE_2D) {
Array msg;
msg.push_back(false);
_put_msg("scene:override_camera_2D:set", msg);
} else if (p_override >= CameraOverride::OVERRIDE_3D_1 && camera_override < CameraOverride::OVERRIDE_3D_1) {
Array msg;
msg.push_back(true);
_put_msg("scene:override_camera_3D:set", msg);
} else if (p_override < CameraOverride::OVERRIDE_3D_1 && camera_override >= CameraOverride::OVERRIDE_3D_1) {
Array msg;
msg.push_back(false);
_put_msg("scene:override_camera_3D:set", msg);
}
Array msg;
msg.push_back(p_override != CameraOverride::OVERRIDE_NONE);
msg.push_back(p_override == CameraOverride::OVERRIDE_EDITORS);
_put_msg("scene:override_cameras", msg);
camera_override = p_override;
}
@ -1757,6 +1786,8 @@ void ScriptEditorDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("remote_tree_updated"));
ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::NODE_PATH, "path")));
ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title")));
ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));
ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));
ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));
@ -1802,6 +1833,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed));
InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));
EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &ScriptEditorDebugger::_resources_reimported));
{ //debugger
VBoxContainer *vbc = memnew(VBoxContainer);
@ -1822,7 +1854,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
hbc->add_child(memnew(VSeparator));
skip_breakpoints = memnew(Button);
skip_breakpoints->set_theme_type_variation("FlatButton");
skip_breakpoints->set_theme_type_variation(SceneStringName(FlatButton));
hbc->add_child(skip_breakpoints);
skip_breakpoints->set_tooltip_text(TTR("Skip Breakpoints"));
skip_breakpoints->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints));
@ -1830,7 +1862,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
hbc->add_child(memnew(VSeparator));
copy = memnew(Button);
copy->set_theme_type_variation("FlatButton");
copy->set_theme_type_variation(SceneStringName(FlatButton));
hbc->add_child(copy);
copy->set_tooltip_text(TTR("Copy Error"));
copy->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_copy));
@ -1838,14 +1870,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
hbc->add_child(memnew(VSeparator));
step = memnew(Button);
step->set_theme_type_variation("FlatButton");
step->set_theme_type_variation(SceneStringName(FlatButton));
hbc->add_child(step);
step->set_tooltip_text(TTR("Step Into"));
step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into"));
step->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_step));
next = memnew(Button);
next->set_theme_type_variation("FlatButton");
next->set_theme_type_variation(SceneStringName(FlatButton));
hbc->add_child(next);
next->set_tooltip_text(TTR("Step Over"));
next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over"));
@ -1854,14 +1886,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
hbc->add_child(memnew(VSeparator));
dobreak = memnew(Button);
dobreak->set_theme_type_variation("FlatButton");
dobreak->set_theme_type_variation(SceneStringName(FlatButton));
hbc->add_child(dobreak);
dobreak->set_tooltip_text(TTR("Break"));
dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break"));
dobreak->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_break));
docontinue = memnew(Button);
docontinue->set_theme_type_variation("FlatButton");
docontinue->set_theme_type_variation(SceneStringName(FlatButton));
hbc->add_child(docontinue);
docontinue->set_tooltip_text(TTR("Continue"));
docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));
@ -1889,16 +1921,19 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
threads->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_select_thread));
stack_dump = memnew(Tree);
stack_dump->set_custom_minimum_size(Size2(150, 0) * EDSCALE);
stack_dump->set_allow_reselect(true);
stack_dump->set_columns(1);
stack_dump->set_column_titles_visible(true);
stack_dump->set_column_title(0, TTR("Stack Frames"));
stack_dump->set_hide_root(true);
stack_dump->set_v_size_flags(SIZE_EXPAND_FILL);
stack_dump->set_theme_type_variation("TreeSecondary");
stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));
stack_vb->add_child(stack_dump);
VBoxContainer *inspector_vbox = memnew(VBoxContainer);
inspector_vbox->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
sc->add_child(inspector_vbox);
@ -1924,12 +1959,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
inspector_vbox->add_child(inspector);
breakpoints_tree = memnew(Tree);
breakpoints_tree->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL);
breakpoints_tree->set_column_titles_visible(true);
breakpoints_tree->set_column_title(0, TTR("Breakpoints"));
breakpoints_tree->set_allow_reselect(true);
breakpoints_tree->set_allow_rmb_select(true);
breakpoints_tree->set_hide_root(true);
breakpoints_tree->set_theme_type_variation("TreeSecondary");
breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected));
breakpoints_tree->create_item();
@ -2002,6 +2039,13 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
add_child(file_dialog);
}
{ // Expression evaluator
expression_evaluator = memnew(EditorExpressionEvaluator);
expression_evaluator->set_name(TTR("Evaluator"));
expression_evaluator->set_editor_debugger(this);
tabs->add_child(expression_evaluator);
}
{ //profiler
profiler = memnew(EditorProfiler);
profiler->set_name(TTR("Profiler"));
@ -2036,10 +2080,10 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
vmem_hb->add_child(vmem_total);
vmem_refresh = memnew(Button);
vmem_refresh->set_theme_type_variation("FlatButton");
vmem_refresh->set_theme_type_variation(SceneStringName(FlatButton));
vmem_hb->add_child(vmem_refresh);
vmem_export = memnew(Button);
vmem_export->set_theme_type_variation("FlatButton");
vmem_export->set_theme_type_variation(SceneStringName(FlatButton));
vmem_export->set_tooltip_text(TTR("Export list to a CSV file"));
vmem_hb->add_child(vmem_export);
vmem_vb->add_child(vmem_hb);

View file

@ -56,6 +56,7 @@ class SceneDebuggerTree;
class EditorDebuggerPlugin;
class DebugAdapterProtocol;
class DebugAdapterParser;
class EditorExpressionEvaluator;
class ScriptEditorDebugger : public MarginContainer {
GDCLASS(ScriptEditorDebugger, MarginContainer);
@ -152,6 +153,7 @@ private:
EditorProfiler *profiler = nullptr;
EditorVisualProfiler *visual_profiler = nullptr;
EditorPerformanceProfiler *performance_profiler = nullptr;
EditorExpressionEvaluator *expression_evaluator = nullptr;
OS::ProcessID remote_pid = 0;
bool move_to_foreground = true;
@ -196,6 +198,8 @@ private:
void _video_mem_request();
void _video_mem_export();
void _resources_reimported(const PackedStringArray &p_resources);
int _get_node_path_cache(const NodePath &p_path);
int _get_res_path_cache(const String &p_path);
@ -252,6 +256,8 @@ public:
void request_remote_tree();
const SceneDebuggerTree *get_remote_tree();
void request_remote_evaluate(const String &p_expression, int p_stack_frame);
void start(Ref<RemoteDebuggerPeer> p_peer);
void stop();