feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
145
engine/editor/debugger/editor_expression_evaluator.cpp
Normal file
145
engine/editor/debugger/editor_expression_evaluator.cpp
Normal 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);
|
||||
}
|
||||
77
engine/editor/debugger/editor_expression_evaluator.h
Normal file
77
engine/editor/debugger/editor_expression_evaluator.h
Normal 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
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue