mirror of
https://github.com/godotengine/godot
synced 2024-09-04 22:38:28 +00:00
Huge Debugger/EditorDebugger refactor.
This commit is contained in:
parent
8b058d4b9a
commit
cbc450c0e5
1145
core/script_debugger_remote.cpp
Normal file
1145
core/script_debugger_remote.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -37,16 +37,170 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
class SceneTree;
|
||||
|
||||
class ScriptDebuggerRemote : public ScriptDebugger {
|
||||
|
||||
struct Message {
|
||||
public:
|
||||
class ResourceInfo {
|
||||
public:
|
||||
String path;
|
||||
String format;
|
||||
String type;
|
||||
RID id;
|
||||
int vram;
|
||||
bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
|
||||
ResourceInfo() {
|
||||
vram = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceUsage {
|
||||
public:
|
||||
List<ResourceInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class FrameInfo {
|
||||
public:
|
||||
StringName name;
|
||||
float self_time;
|
||||
float total_time;
|
||||
|
||||
FrameInfo() {
|
||||
self_time = 0;
|
||||
total_time = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class FrameFunction {
|
||||
public:
|
||||
int sig_id;
|
||||
int call_count;
|
||||
StringName name;
|
||||
float self_time;
|
||||
float total_time;
|
||||
|
||||
FrameFunction() {
|
||||
sig_id = -1;
|
||||
call_count = 0;
|
||||
self_time = 0;
|
||||
total_time = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class ScriptStackVariable {
|
||||
public:
|
||||
String name;
|
||||
Variant value;
|
||||
int type;
|
||||
ScriptStackVariable() {
|
||||
type = -1;
|
||||
}
|
||||
|
||||
Array serialize(int max_size = 1 << 20); // 1 MiB default.
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class ScriptStackDump {
|
||||
public:
|
||||
List<ScriptLanguage::StackInfo> frames;
|
||||
ScriptStackDump() {}
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class Message {
|
||||
|
||||
public:
|
||||
String message;
|
||||
Array data;
|
||||
|
||||
Message() {}
|
||||
};
|
||||
|
||||
class OutputError {
|
||||
public:
|
||||
int hr;
|
||||
int min;
|
||||
int sec;
|
||||
int msec;
|
||||
String source_file;
|
||||
String source_func;
|
||||
int source_line;
|
||||
String error;
|
||||
String error_descr;
|
||||
bool warning;
|
||||
Vector<ScriptLanguage::StackInfo> callstack;
|
||||
|
||||
OutputError() {
|
||||
hr = -1;
|
||||
min = -1;
|
||||
sec = -1;
|
||||
msec = -1;
|
||||
source_line = -1;
|
||||
warning = false;
|
||||
}
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
struct FrameData {
|
||||
|
||||
StringName name;
|
||||
Array data;
|
||||
};
|
||||
|
||||
class ProfilerSignature {
|
||||
public:
|
||||
StringName name;
|
||||
int id;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
|
||||
ProfilerSignature() {
|
||||
id = -1;
|
||||
};
|
||||
};
|
||||
|
||||
class ProfilerFrame {
|
||||
public:
|
||||
int frame_number;
|
||||
float frame_time;
|
||||
float idle_time;
|
||||
float physics_time;
|
||||
float physics_frame_time;
|
||||
float script_time;
|
||||
|
||||
Vector<FrameData> frames_data;
|
||||
Vector<FrameFunction> frame_functions;
|
||||
|
||||
ProfilerFrame() {
|
||||
frame_number = 0;
|
||||
frame_time = 0;
|
||||
idle_time = 0;
|
||||
physics_time = 0;
|
||||
physics_frame_time = 0;
|
||||
}
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class NetworkProfilerFrame {
|
||||
public:
|
||||
Vector<MultiplayerAPI::ProfilingInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
|
||||
NetworkProfilerFrame(){};
|
||||
};
|
||||
|
||||
protected:
|
||||
struct ProfileInfoSort {
|
||||
|
||||
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
|
||||
|
@ -78,21 +232,6 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
|||
bool requested_quit;
|
||||
Mutex *mutex;
|
||||
|
||||
struct OutputError {
|
||||
|
||||
int hr;
|
||||
int min;
|
||||
int sec;
|
||||
int msec;
|
||||
String source_file;
|
||||
String source_func;
|
||||
int source_line;
|
||||
String error;
|
||||
String error_descr;
|
||||
bool warning;
|
||||
Array callstack;
|
||||
};
|
||||
|
||||
List<String> output_strings;
|
||||
List<Message> messages;
|
||||
int max_messages_per_frame;
|
||||
|
@ -119,9 +258,7 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
|||
void _poll_events();
|
||||
uint32_t poll_every;
|
||||
|
||||
SceneTree *scene_tree;
|
||||
|
||||
bool _parse_live_edit(const Array &p_command);
|
||||
void _parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script = NULL);
|
||||
|
||||
void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
|
||||
|
||||
|
@ -133,40 +270,24 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
|||
ErrorHandlerList eh;
|
||||
static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
|
||||
|
||||
void _put_msg(String p_message, Array p_data);
|
||||
void _send_profiling_data(bool p_for_frame);
|
||||
void _send_network_profiling_data();
|
||||
void _send_network_bandwidth_usage();
|
||||
|
||||
struct FrameData {
|
||||
|
||||
StringName name;
|
||||
Array data;
|
||||
};
|
||||
|
||||
Vector<FrameData> profile_frame_data;
|
||||
|
||||
void _put_variable(const String &p_name, const Variant &p_variable);
|
||||
|
||||
void _save_node(ObjectID id, const String &p_path);
|
||||
|
||||
bool skip_breakpoints;
|
||||
|
||||
public:
|
||||
struct ResourceUsage {
|
||||
|
||||
String path;
|
||||
String format;
|
||||
String type;
|
||||
RID id;
|
||||
int vram;
|
||||
bool operator<(const ResourceUsage &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
|
||||
};
|
||||
|
||||
typedef void (*ResourceUsageFunc)(List<ResourceUsage> *);
|
||||
typedef void (*ResourceUsageFunc)(ResourceUsage *);
|
||||
typedef Error (*ParseMessageFunc)(const String &p_name, const Array &p_msg); // Returns true if something was found (stopping propagation).
|
||||
|
||||
static ResourceUsageFunc resource_usage_func;
|
||||
static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
|
||||
|
||||
Error connect_to_host(const String &p_host, uint16_t p_port);
|
||||
bool is_peer_connected();
|
||||
virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
|
||||
virtual void idle_poll();
|
||||
virtual void line_poll();
|
||||
|
@ -188,8 +309,6 @@ public:
|
|||
|
||||
virtual void set_skip_breakpoints(bool p_skip_breakpoints);
|
||||
|
||||
void set_scene_tree(SceneTree *p_scene_tree) { scene_tree = p_scene_tree; };
|
||||
|
||||
ScriptDebuggerRemote();
|
||||
~ScriptDebuggerRemote();
|
||||
};
|
|
@ -82,6 +82,7 @@ if env['tools']:
|
|||
|
||||
SConscript('collada/SCsub')
|
||||
SConscript('doc/SCsub')
|
||||
SConscript('debugger/SCsub')
|
||||
SConscript('fileserver/SCsub')
|
||||
SConscript('icons/SCsub')
|
||||
SConscript('import/SCsub')
|
||||
|
|
5
editor/debugger/SCsub
Normal file
5
editor/debugger/SCsub
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import('env')
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
277
editor/debugger/editor_debugger_inspector.cpp
Normal file
277
editor/debugger/editor_debugger_inspector.cpp
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_inspector.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_debugger_inspector.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
|
||||
bool EditorDebuggerRemoteObject::_set(const StringName &p_name, const Variant &p_value) {
|
||||
|
||||
if (!editable || !prop_values.has(p_name) || String(p_name).begins_with("Constants/"))
|
||||
return false;
|
||||
|
||||
prop_values[p_name] = p_value;
|
||||
emit_signal("value_edited", remote_object_id, p_name, p_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorDebuggerRemoteObject::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
|
||||
if (!prop_values.has(p_name))
|
||||
return false;
|
||||
|
||||
r_ret = prop_values[p_name];
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
|
||||
p_list->clear(); //sorry, no want category
|
||||
for (const List<PropertyInfo>::Element *E = prop_list.front(); E; E = E->next()) {
|
||||
p_list->push_back(E->get());
|
||||
}
|
||||
}
|
||||
|
||||
String EditorDebuggerRemoteObject::get_title() {
|
||||
if (remote_object_id.is_valid())
|
||||
return TTR("Remote ") + String(type_name) + ": " + itos(remote_object_id);
|
||||
else
|
||||
return "<null>";
|
||||
}
|
||||
|
||||
Variant EditorDebuggerRemoteObject::get_variant(const StringName &p_name) {
|
||||
Variant var;
|
||||
_get(p_name, var);
|
||||
return var;
|
||||
}
|
||||
|
||||
void EditorDebuggerRemoteObject::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObject::get_title);
|
||||
ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant);
|
||||
ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear);
|
||||
ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
|
||||
}
|
||||
|
||||
EditorDebuggerInspector::EditorDebuggerInspector() {
|
||||
variables = memnew(EditorDebuggerRemoteObject);
|
||||
variables->editable = false;
|
||||
}
|
||||
|
||||
EditorDebuggerInspector::~EditorDebuggerInspector() {
|
||||
memdelete(variables);
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_object_edited", "name", "value"), &EditorDebuggerInspector::_object_edited);
|
||||
ClassDB::bind_method(D_METHOD("_object_selected", "id"), &EditorDebuggerInspector::_object_selected);
|
||||
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "id")));
|
||||
ADD_SIGNAL(MethodInfo("object_edited", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
|
||||
ADD_SIGNAL(MethodInfo("object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_POSTINITIALIZE:
|
||||
connect_compat("object_id_selected", this, "_object_selected");
|
||||
break;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
edit(variables);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) {
|
||||
|
||||
emit_signal("object_edited", p_id, p_prop, p_value);
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::_object_selected(ObjectID p_object) {
|
||||
|
||||
emit_signal("object_selected", p_object);
|
||||
}
|
||||
|
||||
ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
|
||||
EditorDebuggerRemoteObject *debugObj = NULL;
|
||||
|
||||
SceneDebuggerObject obj;
|
||||
obj.deserialize(p_arr);
|
||||
ERR_FAIL_COND_V(obj.id.is_null(), ObjectID());
|
||||
|
||||
if (remote_objects.has(obj.id)) {
|
||||
debugObj = remote_objects[obj.id];
|
||||
} else {
|
||||
debugObj = memnew(EditorDebuggerRemoteObject);
|
||||
debugObj->remote_object_id = obj.id;
|
||||
debugObj->type_name = obj.class_name;
|
||||
remote_objects[obj.id] = debugObj;
|
||||
debugObj->connect_compat("value_edited", this, "_object_edited");
|
||||
}
|
||||
|
||||
int old_prop_size = debugObj->prop_list.size();
|
||||
|
||||
debugObj->prop_list.clear();
|
||||
int new_props_added = 0;
|
||||
Set<String> changed;
|
||||
for (int i = 0; i < obj.properties.size(); i++) {
|
||||
|
||||
PropertyInfo &pinfo = obj.properties[i].first;
|
||||
Variant &var = obj.properties[i].second;
|
||||
|
||||
if (pinfo.type == Variant::OBJECT) {
|
||||
if (var.get_type() == Variant::STRING) {
|
||||
String path = var;
|
||||
if (path.find("::") != -1) {
|
||||
// built-in resource
|
||||
String base_path = path.get_slice("::", 0);
|
||||
if (ResourceLoader::get_resource_type(base_path) == "PackedScene") {
|
||||
if (!EditorNode::get_singleton()->is_scene_open(base_path)) {
|
||||
EditorNode::get_singleton()->load_scene(base_path);
|
||||
}
|
||||
} else {
|
||||
EditorNode::get_singleton()->load_resource(base_path);
|
||||
}
|
||||
}
|
||||
var = ResourceLoader::load(path);
|
||||
|
||||
if (pinfo.hint_string == "Script") {
|
||||
if (debugObj->get_script() != var) {
|
||||
debugObj->set_script(REF());
|
||||
Ref<Script> script(var);
|
||||
if (!script.is_null()) {
|
||||
ScriptInstance *script_instance = script->placeholder_instance_create(debugObj);
|
||||
debugObj->set_script_and_instance(var, script_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//always add the property, since props may have been added or removed
|
||||
debugObj->prop_list.push_back(pinfo);
|
||||
|
||||
if (!debugObj->prop_values.has(pinfo.name)) {
|
||||
new_props_added++;
|
||||
debugObj->prop_values[pinfo.name] = var;
|
||||
} else {
|
||||
|
||||
if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) {
|
||||
debugObj->prop_values[pinfo.name] = var;
|
||||
changed.insert(pinfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) {
|
||||
//only some may have changed, if so, then update those, if exist
|
||||
for (Set<String>::Element *E = changed.front(); E; E = E->next()) {
|
||||
emit_signal("object_property_updated", debugObj->remote_object_id, E->get());
|
||||
}
|
||||
} else {
|
||||
//full update, because props were added or removed
|
||||
debugObj->update();
|
||||
}
|
||||
return obj.id;
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::clear_cache() {
|
||||
for (Map<ObjectID, EditorDebuggerRemoteObject *>::Element *E = remote_objects.front(); E; E = E->next()) {
|
||||
EditorNode *editor = EditorNode::get_singleton();
|
||||
if (editor->get_editor_history()->get_current() == E->value()->get_instance_id()) {
|
||||
editor->push_item(NULL);
|
||||
}
|
||||
memdelete(E->value());
|
||||
}
|
||||
remote_objects.clear();
|
||||
}
|
||||
|
||||
Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
|
||||
if (remote_objects.has(p_id))
|
||||
return remote_objects[p_id];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
|
||||
|
||||
ScriptDebuggerRemote::ScriptStackVariable var;
|
||||
var.deserialize(p_array);
|
||||
String n = var.name;
|
||||
Variant v = var.value;
|
||||
|
||||
PropertyHint h = PROPERTY_HINT_NONE;
|
||||
String hs = String();
|
||||
|
||||
if (v.get_type() == Variant::OBJECT) {
|
||||
v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id();
|
||||
h = PROPERTY_HINT_OBJECT_ID;
|
||||
hs = "Object";
|
||||
}
|
||||
String type;
|
||||
switch (var.type) {
|
||||
case 0:
|
||||
type = "Locals/";
|
||||
break;
|
||||
case 1:
|
||||
type = "Members/";
|
||||
break;
|
||||
case 2:
|
||||
type = "Globals/";
|
||||
break;
|
||||
default:
|
||||
type = "Unknown/";
|
||||
}
|
||||
|
||||
PropertyInfo pinfo;
|
||||
pinfo.name = type + n;
|
||||
pinfo.type = v.get_type();
|
||||
pinfo.hint = h;
|
||||
pinfo.hint_string = hs;
|
||||
|
||||
variables->prop_list.push_back(pinfo);
|
||||
variables->prop_values[type + n] = v;
|
||||
variables->update();
|
||||
edit(variables);
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::clear_stack_variables() {
|
||||
variables->clear();
|
||||
variables->update();
|
||||
}
|
||||
|
||||
String EditorDebuggerInspector::get_stack_variable(const String &p_var) {
|
||||
return variables->get_variant(p_var);
|
||||
}
|
98
editor/debugger/editor_debugger_inspector.h
Normal file
98
editor/debugger/editor_debugger_inspector.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_inspector.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_DEBUGGER_INSPECTOR_H
|
||||
#define EDITOR_DEBUGGER_INSPECTOR_H
|
||||
#include "editor/editor_inspector.h"
|
||||
|
||||
class EditorDebuggerRemoteObject : public Object {
|
||||
|
||||
GDCLASS(EditorDebuggerRemoteObject, Object);
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
bool editable = false;
|
||||
ObjectID remote_object_id;
|
||||
String type_name;
|
||||
List<PropertyInfo> prop_list;
|
||||
Map<StringName, Variant> prop_values;
|
||||
|
||||
ObjectID get_remote_object_id() { return remote_object_id; };
|
||||
String get_title();
|
||||
|
||||
Variant get_variant(const StringName &p_name);
|
||||
|
||||
void clear() {
|
||||
prop_list.clear();
|
||||
prop_values.clear();
|
||||
}
|
||||
|
||||
void update() { _change_notify(); }
|
||||
|
||||
EditorDebuggerRemoteObject(){};
|
||||
};
|
||||
|
||||
class EditorDebuggerInspector : public EditorInspector {
|
||||
|
||||
GDCLASS(EditorDebuggerInspector, EditorInspector);
|
||||
|
||||
private:
|
||||
ObjectID inspected_object_id;
|
||||
Map<ObjectID, EditorDebuggerRemoteObject *> remote_objects;
|
||||
EditorDebuggerRemoteObject *variables;
|
||||
|
||||
void _object_selected(ObjectID p_object);
|
||||
void _object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
EditorDebuggerInspector();
|
||||
~EditorDebuggerInspector();
|
||||
|
||||
// Remote Object cache
|
||||
ObjectID add_object(const Array &p_arr);
|
||||
Object *get_object(ObjectID p_id);
|
||||
void clear_cache();
|
||||
|
||||
// Stack Dump variables
|
||||
String get_stack_variable(const String &p_var);
|
||||
void add_stack_variable(const Array &p_arr);
|
||||
void clear_stack_variables();
|
||||
};
|
||||
|
||||
#endif // EDITOR_DEBUGGER_INSPECTOR_H
|
564
editor/debugger/editor_debugger_node.cpp
Normal file
564
editor/debugger/editor_debugger_node.cpp
Normal file
|
@ -0,0 +1,564 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_node.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_debugger_node.h"
|
||||
|
||||
#include "editor/debugger/editor_debugger_tree.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
|
||||
template <typename Func>
|
||||
void _for_all(EditorDebuggerNode *p_node, const Func &p_func) {
|
||||
for (int i = 0; i < p_node->get_tab_count(); i++) {
|
||||
ScriptEditorDebugger *dbg = Object::cast_to<ScriptEditorDebugger>(p_node->get_tab_control(i));
|
||||
ERR_FAIL_COND(!dbg);
|
||||
p_func(dbg);
|
||||
}
|
||||
}
|
||||
|
||||
EditorDebuggerNode *EditorDebuggerNode::singleton = NULL;
|
||||
|
||||
EditorDebuggerNode::EditorDebuggerNode() {
|
||||
if (!singleton)
|
||||
singleton = this;
|
||||
server.instance();
|
||||
EditorNode *editor = EditorNode::get_singleton();
|
||||
set_tab_align(TabAlign::ALIGN_LEFT);
|
||||
add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
|
||||
add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
|
||||
add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
|
||||
|
||||
auto_switch_remote_scene_tree = EDITOR_DEF("debugger/auto_switch_to_remote_scene_tree", false);
|
||||
_add_debugger("Debugger");
|
||||
|
||||
// Remote scene tree
|
||||
remote_scene_tree = memnew(EditorDebuggerTree);
|
||||
remote_scene_tree->connect_compat("object_selected", this, "_remote_object_requested");
|
||||
remote_scene_tree->connect_compat("save_node", this, "_save_node_requested");
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(remote_scene_tree);
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->connect_compat("remote_tree_selected", this, "request_remote_tree");
|
||||
|
||||
remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0);
|
||||
inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
|
||||
|
||||
editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this);
|
||||
editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this);
|
||||
editor->get_pause_button()->connect_compat("pressed", this, "_paused");
|
||||
}
|
||||
|
||||
ScriptEditorDebugger *EditorDebuggerNode::_add_debugger(String p_name) {
|
||||
ScriptEditorDebugger *node = memnew(ScriptEditorDebugger(EditorNode::get_singleton()));
|
||||
node->set_name(p_name);
|
||||
int id = get_tab_count();
|
||||
node->connect_compat("stop_requested", this, "_debugger_wants_stop", varray(id));
|
||||
node->connect_compat("stopped", this, "_debugger_stopped", varray(id));
|
||||
node->connect_compat("stack_frame_selected", this, "_stack_frame_selected", varray(id));
|
||||
node->connect_compat("error_selected", this, "_error_selected", varray(id));
|
||||
node->connect_compat("clear_execution", this, "_clear_execution");
|
||||
node->connect_compat("breaked", this, "_breaked", varray(id));
|
||||
node->connect_compat("remote_tree_updated", this, "_remote_tree_updated", varray(id));
|
||||
node->connect_compat("remote_object_updated", this, "_remote_object_updated", varray(id));
|
||||
node->connect_compat("remote_object_property_updated", this, "_remote_object_property_updated", varray(id));
|
||||
node->connect_compat("remote_object_requested", this, "_remote_object_requested", varray(id));
|
||||
add_child(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_stack_frame_selected(int p_debugger) {
|
||||
const ScriptEditorDebugger *dbg = get_debugger(p_debugger);
|
||||
ERR_FAIL_COND(!dbg);
|
||||
if (dbg != get_current_debugger())
|
||||
return;
|
||||
_text_editor_stack_goto(dbg);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_error_selected(const String &p_file, int p_line, int p_debugger) {
|
||||
Ref<Script> s = ResourceLoader::load(p_file);
|
||||
emit_signal("goto_script_line", s, p_line - 1);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_debugger) {
|
||||
const String file = p_debugger->get_stack_script_file();
|
||||
if (file.empty())
|
||||
return;
|
||||
stack_script = ResourceLoader::load(file);
|
||||
const int line = p_debugger->get_stack_script_line() - 1;
|
||||
emit_signal("goto_script_line", stack_script, line);
|
||||
emit_signal("set_execution", stack_script, line);
|
||||
stack_script.unref(); // Why?!?
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_bind_methods() {
|
||||
ClassDB::bind_method("_menu_option", &EditorDebuggerNode::_menu_option);
|
||||
ClassDB::bind_method("_debugger_stopped", &EditorDebuggerNode::_debugger_stopped);
|
||||
ClassDB::bind_method("_debugger_wants_stop", &EditorDebuggerNode::_debugger_wants_stop);
|
||||
ClassDB::bind_method("_debugger_changed", &EditorDebuggerNode::_debugger_changed);
|
||||
ClassDB::bind_method("_stack_frame_selected", &EditorDebuggerNode::_stack_frame_selected);
|
||||
ClassDB::bind_method("_error_selected", &EditorDebuggerNode::_error_selected);
|
||||
ClassDB::bind_method("_clear_execution", &EditorDebuggerNode::_clear_execution);
|
||||
ClassDB::bind_method("_breaked", &EditorDebuggerNode::_breaked);
|
||||
ClassDB::bind_method("start", &EditorDebuggerNode::start);
|
||||
ClassDB::bind_method("stop", &EditorDebuggerNode::stop);
|
||||
ClassDB::bind_method("_paused", &EditorDebuggerNode::_paused);
|
||||
ClassDB::bind_method("request_remote_tree", &EditorDebuggerNode::request_remote_tree);
|
||||
ClassDB::bind_method("_remote_tree_updated", &EditorDebuggerNode::_remote_tree_updated);
|
||||
ClassDB::bind_method("_remote_object_updated", &EditorDebuggerNode::_remote_object_updated);
|
||||
ClassDB::bind_method("_remote_object_property_updated", &EditorDebuggerNode::_remote_object_property_updated);
|
||||
ClassDB::bind_method("_remote_object_requested", &EditorDebuggerNode::_remote_object_requested);
|
||||
ClassDB::bind_method("_save_node_requested", &EditorDebuggerNode::_save_node_requested);
|
||||
|
||||
// LiveDebug.
|
||||
ClassDB::bind_method("live_debug_create_node", &EditorDebuggerNode::live_debug_create_node);
|
||||
ClassDB::bind_method("live_debug_instance_node", &EditorDebuggerNode::live_debug_instance_node);
|
||||
ClassDB::bind_method("live_debug_remove_node", &EditorDebuggerNode::live_debug_remove_node);
|
||||
ClassDB::bind_method("live_debug_remove_and_keep_node", &EditorDebuggerNode::live_debug_remove_and_keep_node);
|
||||
ClassDB::bind_method("live_debug_restore_node", &EditorDebuggerNode::live_debug_restore_node);
|
||||
ClassDB::bind_method("live_debug_duplicate_node", &EditorDebuggerNode::live_debug_duplicate_node);
|
||||
ClassDB::bind_method("live_debug_reparent_node", &EditorDebuggerNode::live_debug_reparent_node);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("goto_script_line"));
|
||||
ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));
|
||||
ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));
|
||||
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug")));
|
||||
}
|
||||
|
||||
EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
|
||||
return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_current()));
|
||||
}
|
||||
|
||||
ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const {
|
||||
return Object::cast_to<ScriptEditorDebugger>(get_tab_control(p_id));
|
||||
}
|
||||
|
||||
ScriptEditorDebugger *EditorDebuggerNode::get_current_debugger() const {
|
||||
return Object::cast_to<ScriptEditorDebugger>(get_tab_control(get_current_tab()));
|
||||
}
|
||||
|
||||
ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {
|
||||
return Object::cast_to<ScriptEditorDebugger>(get_tab_control(0));
|
||||
}
|
||||
|
||||
Error EditorDebuggerNode::start() {
|
||||
stop();
|
||||
if (EDITOR_GET("run/output/always_open_output_on_play")) {
|
||||
EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log());
|
||||
} else {
|
||||
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
|
||||
}
|
||||
|
||||
int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
const Error err = server->listen(remote_port);
|
||||
if (err != OK) {
|
||||
EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR);
|
||||
return err;
|
||||
}
|
||||
set_process(true);
|
||||
EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::stop() {
|
||||
if (server->is_listening()) {
|
||||
server->stop();
|
||||
EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
|
||||
}
|
||||
// Also close all debugging sessions.
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
if (dbg->is_session_active())
|
||||
dbg->stop();
|
||||
});
|
||||
_break_state_changed();
|
||||
if (hide_on_stop) {
|
||||
if (is_visible_in_tree())
|
||||
EditorNode::get_singleton()->hide_bottom_panel();
|
||||
}
|
||||
breakpoints.clear();
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_POSTINITIALIZE: {
|
||||
connect_compat("tab_changed", this, "_debugger_changed");
|
||||
} break;
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
EditorNode::get_singleton()->connect_compat("play_pressed", this, "start");
|
||||
EditorNode::get_singleton()->connect_compat("stop_pressed", this, "stop");
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
EditorNode::get_singleton()->disconnect_compat("play_pressed", this, "start");
|
||||
EditorNode::get_singleton()->disconnect_compat("stop_pressed", this, "stop");
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_what != NOTIFICATION_PROCESS || !server->is_listening())
|
||||
return;
|
||||
|
||||
// Errors and warnings
|
||||
int error_count = 0;
|
||||
int warning_count = 0;
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
error_count += dbg->get_error_count();
|
||||
warning_count += dbg->get_warning_count();
|
||||
});
|
||||
|
||||
if (error_count != last_error_count || warning_count != last_warning_count) {
|
||||
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->update_tabs();
|
||||
});
|
||||
|
||||
if (error_count == 0 && warning_count == 0) {
|
||||
debugger_button->set_text(TTR("Debugger"));
|
||||
debugger_button->set_icon(Ref<Texture2D>());
|
||||
} else {
|
||||
debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
|
||||
if (error_count == 0) {
|
||||
debugger_button->set_icon(get_icon("Warning", "EditorIcons"));
|
||||
} else {
|
||||
debugger_button->set_icon(get_icon("Error", "EditorIcons"));
|
||||
}
|
||||
}
|
||||
last_error_count = error_count;
|
||||
last_warning_count = warning_count;
|
||||
}
|
||||
|
||||
// Remote scene tree update
|
||||
remote_scene_tree_timeout -= get_process_delta_time();
|
||||
if (remote_scene_tree_timeout < 0) {
|
||||
remote_scene_tree_timeout = EditorSettings::get_singleton()->get("debugger/remote_scene_tree_refresh_interval");
|
||||
if (remote_scene_tree->is_visible_in_tree()) {
|
||||
get_current_debugger()->request_remote_tree();
|
||||
}
|
||||
}
|
||||
|
||||
// Remote inspector update
|
||||
inspect_edited_object_timeout -= get_process_delta_time();
|
||||
if (inspect_edited_object_timeout < 0) {
|
||||
inspect_edited_object_timeout = EditorSettings::get_singleton()->get("debugger/remote_inspect_refresh_interval");
|
||||
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
|
||||
get_current_debugger()->request_remote_object(obj->remote_object_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Take connections.
|
||||
if (server->is_connection_available()) {
|
||||
ScriptEditorDebugger *debugger = NULL;
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
if (debugger || dbg->is_session_active())
|
||||
return;
|
||||
debugger = dbg;
|
||||
});
|
||||
if (debugger == NULL) {
|
||||
if (get_tab_count() <= 4) { // Max 4 debugging sessions active.
|
||||
debugger = _add_debugger("Session " + itos(get_tab_count()));
|
||||
} else {
|
||||
// We already have too many sessions, disconnecting new clients to prevent it from hanging.
|
||||
// (Not keeping a reference to the connection will disconnect it)
|
||||
server->take_connection();
|
||||
return; // Can't add, stop here.
|
||||
}
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->get_pause_button()->set_disabled(false);
|
||||
// Switch to remote tree view if so desired.
|
||||
auto_switch_remote_scene_tree = (bool)EditorSettings::get_singleton()->get("debugger/auto_switch_to_remote_scene_tree");
|
||||
if (auto_switch_remote_scene_tree) {
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->show_remote_tree();
|
||||
}
|
||||
// Good to go.
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons();
|
||||
debugger->set_editor_remote_tree(remote_scene_tree);
|
||||
debugger->start(server->take_connection());
|
||||
// Send breakpoints.
|
||||
for (Map<Breakpoint, bool>::Element *E = breakpoints.front(); E; E = E->next()) {
|
||||
const Breakpoint &bp = E->key();
|
||||
debugger->set_breakpoint(bp.source, bp.line, E->get());
|
||||
} // Will arrive too late, how does the regular run work?
|
||||
|
||||
debugger->update_live_edit_root();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_debugger_stopped(int p_id) {
|
||||
ScriptEditorDebugger *dbg = get_debugger(p_id);
|
||||
ERR_FAIL_COND(!dbg);
|
||||
|
||||
bool found = false;
|
||||
_for_all(this, [&](ScriptEditorDebugger *p_debugger) {
|
||||
if (p_debugger->is_session_active())
|
||||
found = true;
|
||||
});
|
||||
if (!found) {
|
||||
EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
|
||||
EditorNode::get_singleton()->get_pause_button()->set_disabled(true);
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree();
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons();
|
||||
EditorNode::get_singleton()->notify_all_debug_sessions_exited();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_debugger_wants_stop(int p_id) {
|
||||
// Ask editor to kill PID.
|
||||
int pid = get_debugger(p_id)->get_remote_pid();
|
||||
if (pid)
|
||||
EditorNode::get_singleton()->call_deferred("stop_child_process", pid);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_debugger_changed(int p_tab) {
|
||||
if (get_inspected_remote_object()) {
|
||||
// Clear inspected object, you can only inspect objects in selected debugger.
|
||||
// Hopefully, in the future, we will have one inspector per debugger.
|
||||
EditorNode::get_singleton()->push_item(NULL);
|
||||
}
|
||||
if (remote_scene_tree->is_visible_in_tree()) {
|
||||
get_current_debugger()->request_remote_tree();
|
||||
}
|
||||
if (get_current_debugger()->is_breaked()) {
|
||||
_text_editor_stack_goto(get_current_debugger());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) {
|
||||
script_menu = p_button;
|
||||
script_menu->set_text(TTR("Debug"));
|
||||
script_menu->set_switch_on_hover(true);
|
||||
PopupMenu *p = script_menu->get_popup();
|
||||
p->set_hide_on_window_lose_focus(true);
|
||||
p->add_shortcut(ED_GET_SHORTCUT("debugger/step_into"), DEBUG_STEP);
|
||||
p->add_shortcut(ED_GET_SHORTCUT("debugger/step_over"), DEBUG_NEXT);
|
||||
p->add_separator();
|
||||
p->add_shortcut(ED_GET_SHORTCUT("debugger/break"), DEBUG_BREAK);
|
||||
p->add_shortcut(ED_GET_SHORTCUT("debugger/continue"), DEBUG_CONTINUE);
|
||||
p->add_separator();
|
||||
p->add_check_shortcut(ED_GET_SHORTCUT("debugger/keep_debugger_open"), DEBUG_SHOW_KEEP_OPEN);
|
||||
p->add_check_shortcut(ED_GET_SHORTCUT("debugger/debug_with_external_editor"), DEBUG_WITH_EXTERNAL_EDITOR);
|
||||
p->connect_compat("id_pressed", this, "_menu_option");
|
||||
|
||||
_break_state_changed();
|
||||
script_menu->show();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_break_state_changed() {
|
||||
const bool breaked = get_current_debugger()->is_breaked();
|
||||
const bool can_debug = get_current_debugger()->is_debuggable();
|
||||
if (breaked) // Show debugger.
|
||||
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
|
||||
|
||||
// Update script menu.
|
||||
if (!script_menu)
|
||||
return;
|
||||
PopupMenu *p = script_menu->get_popup();
|
||||
p->set_item_disabled(p->get_item_index(DEBUG_NEXT), !(breaked && can_debug));
|
||||
p->set_item_disabled(p->get_item_index(DEBUG_STEP), !(breaked && can_debug));
|
||||
p->set_item_disabled(p->get_item_index(DEBUG_BREAK), breaked);
|
||||
p->set_item_disabled(p->get_item_index(DEBUG_CONTINUE), !breaked);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_menu_option(int p_id) {
|
||||
switch (p_id) {
|
||||
case DEBUG_NEXT: {
|
||||
debug_next();
|
||||
} break;
|
||||
case DEBUG_STEP: {
|
||||
debug_step();
|
||||
} break;
|
||||
case DEBUG_BREAK: {
|
||||
debug_break();
|
||||
} break;
|
||||
case DEBUG_CONTINUE: {
|
||||
debug_continue();
|
||||
} break;
|
||||
|
||||
case DEBUG_SHOW_KEEP_OPEN: {
|
||||
bool visible = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN));
|
||||
hide_on_stop = visible;
|
||||
script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
|
||||
} break;
|
||||
case DEBUG_WITH_EXTERNAL_EDITOR: {
|
||||
bool checked = !script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));
|
||||
debug_with_external_editor = checked;
|
||||
script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), checked);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_paused() {
|
||||
const bool paused = EditorNode::get_singleton()->get_pause_button()->is_pressed();
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
if (paused && !dbg->is_breaked()) {
|
||||
dbg->debug_break();
|
||||
} else if (!paused && dbg->is_breaked()) {
|
||||
dbg->debug_continue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, int p_debugger) {
|
||||
if (get_current_debugger() != get_debugger(p_debugger)) {
|
||||
if (!p_breaked)
|
||||
return;
|
||||
set_current_tab(p_debugger);
|
||||
}
|
||||
_break_state_changed();
|
||||
EditorNode::get_singleton()->get_pause_button()->set_pressed(p_breaked);
|
||||
emit_signal("breaked", p_breaked, p_can_debug);
|
||||
}
|
||||
|
||||
bool EditorDebuggerNode::is_skip_breakpoints() const {
|
||||
return get_default_debugger()->is_skip_breakpoints();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
|
||||
breakpoints[Breakpoint(p_path, p_line)] = p_enabled;
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->set_breakpoint(p_path, p_line, p_enabled);
|
||||
});
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::reload_scripts() {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->reload_scripts();
|
||||
});
|
||||
}
|
||||
|
||||
// LiveEdit/Inspector
|
||||
void EditorDebuggerNode::request_remote_tree() {
|
||||
get_current_debugger()->request_remote_tree();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
|
||||
if (p_debugger != get_current_tab())
|
||||
return;
|
||||
remote_scene_tree->clear();
|
||||
remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) {
|
||||
if (p_debugger != get_current_tab())
|
||||
return;
|
||||
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
|
||||
if (obj->remote_object_id == p_id)
|
||||
return; // Already being edited
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->push_item(get_current_debugger()->get_remote_object(p_id));
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) {
|
||||
if (p_debugger != get_current_tab())
|
||||
return;
|
||||
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
|
||||
if (obj->remote_object_id != p_id)
|
||||
return;
|
||||
EditorNode::get_singleton()->get_inspector()->update_property(p_property);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_remote_object_requested(ObjectID p_id, int p_debugger) {
|
||||
if (p_debugger != get_current_tab())
|
||||
return;
|
||||
inspect_edited_object_timeout = 0.7; // Temporarily disable timeout to avoid multiple requests.
|
||||
get_current_debugger()->request_remote_object(p_id);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) {
|
||||
if (p_debugger != get_current_tab())
|
||||
return;
|
||||
get_current_debugger()->save_node(p_id, p_file);
|
||||
}
|
||||
|
||||
// Remote inspector/edit.
|
||||
void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) {
|
||||
if (!singleton)
|
||||
return;
|
||||
_for_all(singleton, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->_method_changed(p_base, p_name, VARIANT_ARG_PASS);
|
||||
});
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
|
||||
if (!singleton)
|
||||
return;
|
||||
_for_all(singleton, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->_property_changed(p_base, p_property, p_value);
|
||||
});
|
||||
}
|
||||
|
||||
// LiveDebug
|
||||
void EditorDebuggerNode::set_live_debugging(bool p_enabled) {
|
||||
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->set_live_debugging(p_enabled);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::update_live_edit_root() {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->update_live_edit_root();
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_create_node(p_parent, p_type, p_name);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_instance_node(p_parent, p_path, p_name);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_remove_node(const NodePath &p_at) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_remove_node(p_at);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_remove_and_keep_node(p_at, p_keep_id);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_restore_node(p_id, p_at, p_at_pos);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_duplicate_node(p_at, p_new_name);
|
||||
});
|
||||
}
|
||||
void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
|
||||
_for_all(this, [&](ScriptEditorDebugger *dbg) {
|
||||
dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);
|
||||
});
|
||||
}
|
176
editor/debugger/editor_debugger_node.h
Normal file
176
editor/debugger/editor_debugger_node.h
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_node.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_DEBUGGER_NODE_H
|
||||
#define EDITOR_DEBUGGER_NODE_H
|
||||
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
|
||||
class EditorDebuggerTree;
|
||||
|
||||
class EditorDebuggerNode : public TabContainer {
|
||||
|
||||
GDCLASS(EditorDebuggerNode, TabContainer);
|
||||
|
||||
private:
|
||||
enum Options {
|
||||
DEBUG_NEXT,
|
||||
DEBUG_STEP,
|
||||
DEBUG_BREAK,
|
||||
DEBUG_CONTINUE,
|
||||
DEBUG_SHOW_KEEP_OPEN,
|
||||
DEBUG_WITH_EXTERNAL_EDITOR,
|
||||
};
|
||||
|
||||
class Breakpoint {
|
||||
public:
|
||||
String source;
|
||||
int line = 0;
|
||||
|
||||
bool operator<(const Breakpoint &p_b) const {
|
||||
if (line == p_b.line)
|
||||
return line < p_b.line;
|
||||
return line < p_b.line;
|
||||
}
|
||||
|
||||
Breakpoint(){};
|
||||
|
||||
Breakpoint(const String &p_source, int p_line) {
|
||||
line = p_line;
|
||||
source = p_source;
|
||||
}
|
||||
};
|
||||
|
||||
Ref<TCP_Server> server = NULL;
|
||||
Button *debugger_button = NULL;
|
||||
MenuButton *script_menu = NULL;
|
||||
|
||||
Ref<Script> stack_script; // Why?!?
|
||||
|
||||
int last_error_count = 0;
|
||||
int last_warning_count = 0;
|
||||
|
||||
float inspect_edited_object_timeout = 0;
|
||||
EditorDebuggerTree *remote_scene_tree = NULL;
|
||||
float remote_scene_tree_timeout = 0.0;
|
||||
bool auto_switch_remote_scene_tree = false;
|
||||
bool debug_with_external_editor = false;
|
||||
bool hide_on_stop = true;
|
||||
ScriptEditorDebugger::CameraOverride camera_override = ScriptEditorDebugger::OVERRIDE_NONE;
|
||||
Map<Breakpoint, bool> breakpoints;
|
||||
|
||||
ScriptEditorDebugger *_add_debugger(String p_name);
|
||||
EditorDebuggerRemoteObject *get_inspected_remote_object();
|
||||
|
||||
friend class DebuggerEditorPlugin;
|
||||
static EditorDebuggerNode *singleton;
|
||||
EditorDebuggerNode();
|
||||
|
||||
protected:
|
||||
void _debugger_stopped(int p_id);
|
||||
void _debugger_wants_stop(int p_id);
|
||||
void _debugger_changed(int p_tab);
|
||||
void _remote_tree_updated(int p_debugger);
|
||||
void _remote_object_updated(ObjectID p_id, int p_debugger);
|
||||
void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger);
|
||||
void _remote_object_requested(ObjectID p_id, int p_debugger);
|
||||
void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger);
|
||||
|
||||
void _clear_execution(REF p_script) {
|
||||
emit_signal("clear_execution", p_script);
|
||||
}
|
||||
|
||||
void _text_editor_stack_goto(const ScriptEditorDebugger *p_debugger);
|
||||
void _stack_frame_selected(int p_debugger);
|
||||
void _error_selected(const String &p_file, int p_line, int p_debugger);
|
||||
void _breaked(bool p_breaked, bool p_can_debug, int p_debugger);
|
||||
void _paused();
|
||||
void _break_state_changed();
|
||||
void _menu_option(int p_id);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static EditorDebuggerNode *get_singleton() { return singleton; }
|
||||
|
||||
ScriptEditorDebugger *get_current_debugger() const;
|
||||
ScriptEditorDebugger *get_default_debugger() const;
|
||||
ScriptEditorDebugger *get_debugger(int p_debugger) const;
|
||||
|
||||
void debug_next() { get_default_debugger()->debug_next(); }
|
||||
void debug_step() { get_default_debugger()->debug_step(); }
|
||||
void debug_break() { get_default_debugger()->debug_break(); }
|
||||
void debug_continue() { get_default_debugger()->debug_continue(); }
|
||||
|
||||
void set_script_debug_button(MenuButton *p_button);
|
||||
|
||||
void set_tool_button(Button *p_button) {
|
||||
debugger_button = p_button;
|
||||
}
|
||||
|
||||
String get_var_value(const String &p_var) const { return get_default_debugger()->get_var_value(p_var); }
|
||||
Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this?
|
||||
|
||||
bool get_debug_with_external_editor() { return debug_with_external_editor; }
|
||||
|
||||
bool is_skip_breakpoints() const;
|
||||
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
|
||||
void reload_scripts();
|
||||
|
||||
// Remote inspector/edit.
|
||||
void request_remote_tree();
|
||||
static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE);
|
||||
static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
|
||||
|
||||
// LiveDebug
|
||||
void set_live_debugging(bool p_enabled);
|
||||
void update_live_edit_root();
|
||||
void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name);
|
||||
void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name);
|
||||
void live_debug_remove_node(const NodePath &p_at);
|
||||
void live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id);
|
||||
void live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos);
|
||||
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
|
||||
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
|
||||
|
||||
// Camera
|
||||
void set_camera_override(ScriptEditorDebugger::CameraOverride p_override) { camera_override = p_override; }
|
||||
ScriptEditorDebugger::CameraOverride get_camera_override() { return camera_override; }
|
||||
|
||||
Error start();
|
||||
|
||||
void stop();
|
||||
};
|
||||
#endif // EDITOR_DEBUGGER_NODE_H
|
274
editor/debugger/editor_debugger_tree.cpp
Normal file
274
editor/debugger/editor_debugger_tree.cpp
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_tree.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_debugger_tree.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
EditorDebuggerTree::EditorDebuggerTree() {
|
||||
set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
set_allow_rmb_select(true);
|
||||
|
||||
// Popup
|
||||
item_menu = memnew(PopupMenu);
|
||||
item_menu->connect_compat("id_pressed", this, "_item_menu_id_pressed");
|
||||
add_child(item_menu);
|
||||
|
||||
// File Dialog
|
||||
file_dialog = memnew(EditorFileDialog);
|
||||
file_dialog->connect_compat("file_selected", this, "_file_selected");
|
||||
add_child(file_dialog);
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_POSTINITIALIZE) {
|
||||
connect_compat("cell_selected", this, "_scene_tree_selected");
|
||||
connect_compat("item_collapsed", this, "_scene_tree_folded");
|
||||
connect_compat("item_rmb_selected", this, "_scene_tree_rmb_selected");
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_scene_tree_selected"), &EditorDebuggerTree::_scene_tree_selected);
|
||||
ClassDB::bind_method(D_METHOD("_scene_tree_folded"), &EditorDebuggerTree::_scene_tree_folded);
|
||||
ClassDB::bind_method(D_METHOD("_scene_tree_rmb_selected"), &EditorDebuggerTree::_scene_tree_rmb_selected);
|
||||
ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &EditorDebuggerTree::_item_menu_id_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_file_selected"), &EditorDebuggerTree::_file_selected);
|
||||
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger")));
|
||||
ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger")));
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_scene_tree_selected() {
|
||||
|
||||
if (updating_scene_tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *item = get_selected();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
inspected_object_id = uint64_t(item->get_metadata(0));
|
||||
|
||||
emit_signal("object_selected", inspected_object_id, debugger_id);
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_scene_tree_folded(Object *p_obj) {
|
||||
|
||||
if (updating_scene_tree) {
|
||||
|
||||
return;
|
||||
}
|
||||
TreeItem *item = Object::cast_to<TreeItem>(p_obj);
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
ObjectID id = ObjectID(uint64_t(item->get_metadata(0)));
|
||||
if (unfold_cache.has(id)) {
|
||||
unfold_cache.erase(id);
|
||||
} else {
|
||||
unfold_cache.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) {
|
||||
|
||||
TreeItem *item = get_item_at_position(p_position);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->select(0);
|
||||
|
||||
item_menu->clear();
|
||||
item_menu->add_icon_item(get_icon("CreateNewSceneFrom", "EditorIcons"), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);
|
||||
item_menu->add_icon_item(get_icon("CopyNodePath", "EditorIcons"), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);
|
||||
item_menu->set_global_position(get_global_mouse_position());
|
||||
item_menu->popup();
|
||||
}
|
||||
|
||||
/// Populates inspect_scene_tree given data in nodes as a flat list, encoded depth first.
|
||||
///
|
||||
/// Given a nodes array like [R,A,B,C,D,E] the following Tree will be generated, assuming
|
||||
/// filter is an empty String, R and A child count are 2, B is 1 and C, D and E are 0.
|
||||
///
|
||||
/// R
|
||||
/// |-A
|
||||
/// | |-B
|
||||
/// | | |-C
|
||||
/// | |
|
||||
/// | |-D
|
||||
/// |
|
||||
/// |-E
|
||||
///
|
||||
void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) {
|
||||
updating_scene_tree = true;
|
||||
const String last_path = get_selected_path();
|
||||
const String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter();
|
||||
|
||||
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
|
||||
List<Pair<TreeItem *, int> > parents;
|
||||
for (int i = 0; i < p_tree->nodes.size(); i++) {
|
||||
TreeItem *parent = NULL;
|
||||
if (parents.size()) { // Find last parent.
|
||||
Pair<TreeItem *, int> &p = parents[0];
|
||||
parent = p.first;
|
||||
if (!(--p.second)) { // If no child left, remove it.
|
||||
parents.pop_front();
|
||||
}
|
||||
}
|
||||
// Add this node.
|
||||
const SceneDebuggerTree::RemoteNode &node = p_tree->nodes[i];
|
||||
TreeItem *item = create_item(parent);
|
||||
item->set_text(0, node.name);
|
||||
item->set_tooltip(0, TTR("Type:") + " " + node.type_name);
|
||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(node.type_name, "");
|
||||
if (icon.is_valid()) {
|
||||
item->set_icon(0, icon);
|
||||
}
|
||||
item->set_metadata(0, node.id);
|
||||
|
||||
// Set current item as collapsed if necessary (root is never collapsed)
|
||||
if (parent) {
|
||||
if (!unfold_cache.has(node.id)) {
|
||||
item->set_collapsed(true);
|
||||
}
|
||||
}
|
||||
// Select previously selected node.
|
||||
if (debugger_id == p_debugger) { // Can use remote id.
|
||||
if (node.id == inspected_object_id) {
|
||||
item->select(0);
|
||||
}
|
||||
} else { // Must use path
|
||||
if (last_path == _get_path(item)) {
|
||||
updating_scene_tree = false; // Force emission of new selection
|
||||
item->select(0);
|
||||
updating_scene_tree = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
} else {
|
||||
// Apply filters.
|
||||
while (parent) {
|
||||
const bool had_siblings = item->get_prev() || item->get_next();
|
||||
if (filter.is_subsequence_ofi(item->get_text(0)))
|
||||
break; // Filter matches, must survive.
|
||||
parent->remove_child(item);
|
||||
memdelete(item);
|
||||
if (had_siblings)
|
||||
break; // Parent must survive.
|
||||
item = parent;
|
||||
parent = item->get_parent();
|
||||
// Check if parent expects more children.
|
||||
for (int j = 0; j < parents.size(); j++) {
|
||||
if (parents[j].first == item) {
|
||||
parent = NULL;
|
||||
break; // Might have more children.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree
|
||||
updating_scene_tree = false;
|
||||
}
|
||||
|
||||
String EditorDebuggerTree::get_selected_path() {
|
||||
if (!get_selected())
|
||||
return "";
|
||||
return _get_path(get_selected());
|
||||
}
|
||||
|
||||
String EditorDebuggerTree::_get_path(TreeItem *p_item) {
|
||||
ERR_FAIL_COND_V(!p_item, "");
|
||||
|
||||
if (p_item->get_parent() == NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
|
||||
|
||||
switch (p_option) {
|
||||
|
||||
case ITEM_MENU_SAVE_REMOTE_NODE: {
|
||||
|
||||
file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
|
||||
file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
|
||||
|
||||
List<String> extensions;
|
||||
Ref<PackedScene> sd = memnew(PackedScene);
|
||||
ResourceSaver::get_recognized_extensions(sd, &extensions);
|
||||
file_dialog->clear_filters();
|
||||
for (int i = 0; i < extensions.size(); i++) {
|
||||
file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
|
||||
}
|
||||
|
||||
file_dialog->popup_centered_ratio();
|
||||
} break;
|
||||
case ITEM_MENU_COPY_NODE_PATH: {
|
||||
|
||||
String text = get_selected_path();
|
||||
if (text.empty()) {
|
||||
return;
|
||||
} else if (text == "/root") {
|
||||
text = ".";
|
||||
} else {
|
||||
text = text.replace("/root/", "");
|
||||
int slash = text.find("/");
|
||||
if (slash < 0) {
|
||||
text = ".";
|
||||
} else {
|
||||
text = text.substr(slash + 1);
|
||||
}
|
||||
}
|
||||
OS::get_singleton()->set_clipboard(text);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_file_selected(const String &p_file) {
|
||||
if (inspected_object_id.is_null())
|
||||
return;
|
||||
emit_signal("save_node", inspected_object_id, p_file, debugger_id);
|
||||
}
|
74
editor/debugger/editor_debugger_tree.h
Normal file
74
editor/debugger/editor_debugger_tree.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_tree.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "scene/gui/tree.h"
|
||||
|
||||
#ifndef EDITOR_DEBUGGER_TREE_H
|
||||
#define EDITOR_DEBUGGER_TREE_H
|
||||
|
||||
class SceneDebuggerTree;
|
||||
class EditorFileDialog;
|
||||
|
||||
class EditorDebuggerTree : public Tree {
|
||||
|
||||
GDCLASS(EditorDebuggerTree, Tree);
|
||||
|
||||
private:
|
||||
enum ItemMenu {
|
||||
ITEM_MENU_SAVE_REMOTE_NODE,
|
||||
ITEM_MENU_COPY_NODE_PATH,
|
||||
};
|
||||
|
||||
ObjectID inspected_object_id;
|
||||
int debugger_id = 0;
|
||||
bool updating_scene_tree = false;
|
||||
Set<ObjectID> unfold_cache;
|
||||
PopupMenu *item_menu = NULL;
|
||||
EditorFileDialog *file_dialog = NULL;
|
||||
|
||||
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);
|
||||
void _item_menu_id_pressed(int p_option);
|
||||
void _file_selected(const String &p_file);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
String get_selected_path();
|
||||
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);
|
||||
EditorDebuggerTree();
|
||||
};
|
||||
#endif // EDITOR_DEBUGGER_TREE_H
|
File diff suppressed because it is too large
Load diff
|
@ -32,7 +32,8 @@
|
|||
#define SCRIPT_EDITOR_DEBUGGER_H
|
||||
|
||||
#include "core/io/packet_peer.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "editor/debugger/editor_debugger_inspector.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "scene/3d/camera.h"
|
||||
|
@ -41,7 +42,6 @@
|
|||
|
||||
class Tree;
|
||||
class EditorNode;
|
||||
class ScriptEditorDebuggerVariables;
|
||||
class LineEdit;
|
||||
class TabContainer;
|
||||
class RichTextLabel;
|
||||
|
@ -53,13 +53,14 @@ class ItemList;
|
|||
class EditorProfiler;
|
||||
class EditorVisualProfiler;
|
||||
class EditorNetworkProfiler;
|
||||
|
||||
class ScriptEditorDebuggerInspectedObject;
|
||||
class SceneDebuggerTree;
|
||||
|
||||
class ScriptEditorDebugger : public MarginContainer {
|
||||
|
||||
GDCLASS(ScriptEditorDebugger, MarginContainer);
|
||||
|
||||
friend class EditorDebuggerNode;
|
||||
|
||||
public:
|
||||
enum CameraOverride {
|
||||
OVERRIDE_NONE,
|
||||
|
@ -77,16 +78,8 @@ private:
|
|||
MESSAGE_SUCCESS,
|
||||
};
|
||||
|
||||
enum ItemMenu {
|
||||
ITEM_MENU_COPY_ERROR,
|
||||
ITEM_MENU_SAVE_REMOTE_NODE,
|
||||
ITEM_MENU_COPY_NODE_PATH,
|
||||
};
|
||||
|
||||
AcceptDialog *msgdialog;
|
||||
|
||||
Button *debugger_button;
|
||||
|
||||
LineEdit *clicked_ctrl;
|
||||
LineEdit *clicked_ctrl_type;
|
||||
LineEdit *live_edit_root;
|
||||
|
@ -94,35 +87,15 @@ private:
|
|||
Button *le_clear;
|
||||
Button *export_csv;
|
||||
|
||||
bool updating_scene_tree;
|
||||
float inspect_scene_tree_timeout;
|
||||
float inspect_edited_object_timeout;
|
||||
bool auto_switch_remote_scene_tree;
|
||||
ObjectID inspected_object_id;
|
||||
ScriptEditorDebuggerVariables *variables;
|
||||
Map<ObjectID, ScriptEditorDebuggerInspectedObject *> remote_objects;
|
||||
Set<ObjectID> unfold_cache;
|
||||
|
||||
VBoxContainer *errors_tab;
|
||||
Tree *error_tree;
|
||||
Tree *inspect_scene_tree;
|
||||
Button *clearbutton;
|
||||
PopupMenu *item_menu;
|
||||
|
||||
EditorFileDialog *file_dialog;
|
||||
enum FileDialogMode {
|
||||
SAVE_CSV,
|
||||
SAVE_NODE,
|
||||
};
|
||||
FileDialogMode file_dialog_mode;
|
||||
|
||||
int error_count;
|
||||
int warning_count;
|
||||
int last_error_count;
|
||||
int last_warning_count;
|
||||
|
||||
bool hide_on_stop;
|
||||
bool enable_external_editor;
|
||||
|
||||
bool skip_breakpoints_value = false;
|
||||
Ref<Script> stack_script;
|
||||
|
@ -135,10 +108,11 @@ private:
|
|||
Button *copy;
|
||||
Button *step;
|
||||
Button *next;
|
||||
Button *back;
|
||||
Button *forward;
|
||||
Button *dobreak;
|
||||
Button *docontinue;
|
||||
// Reference to "Remote" tab in scene tree. Needed by _live_edit_set and buttons state.
|
||||
// Each debugger should have it's tree in the future I guess.
|
||||
const Tree *editor_remote_tree = NULL;
|
||||
|
||||
List<Vector<float> > perf_history;
|
||||
Vector<float> perf_max;
|
||||
|
@ -155,16 +129,12 @@ private:
|
|||
LineEdit *vmem_total;
|
||||
|
||||
Tree *stack_dump;
|
||||
EditorInspector *inspector;
|
||||
EditorDebuggerInspector *inspector;
|
||||
SceneDebuggerTree *scene_tree;
|
||||
|
||||
Ref<TCP_Server> server;
|
||||
Ref<StreamPeerTCP> connection;
|
||||
Ref<PacketPeerStream> ppeer;
|
||||
|
||||
String message_type;
|
||||
Array message;
|
||||
int pending_in_queue;
|
||||
|
||||
HashMap<NodePath, int> node_path_cache;
|
||||
int last_path_id;
|
||||
Map<String, int> res_path_cache;
|
||||
|
@ -175,7 +145,9 @@ private:
|
|||
|
||||
EditorNode *editor;
|
||||
|
||||
bool breaked;
|
||||
OS::ProcessID remote_pid = 0;
|
||||
bool breaked = false;
|
||||
bool can_debug = false;
|
||||
|
||||
bool live_debug;
|
||||
|
||||
|
@ -184,18 +156,14 @@ private:
|
|||
void _performance_draw();
|
||||
void _performance_select();
|
||||
void _stack_dump_frame_selected();
|
||||
void _output_clear();
|
||||
|
||||
void _scene_tree_folded(Object *obj);
|
||||
void _scene_tree_selected();
|
||||
void _scene_tree_rmb_selected(const Vector2 &p_position);
|
||||
void _file_selected(const String &p_file);
|
||||
void _scene_tree_request();
|
||||
void _parse_message(const String &p_msg, const Array &p_data);
|
||||
void _set_reason_text(const String &p_reason, MessageType p_type);
|
||||
void _scene_tree_property_select_object(ObjectID p_object);
|
||||
void _scene_tree_property_value_edited(const String &p_prop, const Variant &p_value);
|
||||
int _update_scene_tree(TreeItem *parent, const Array &nodes, int current_index);
|
||||
void _update_buttons_state();
|
||||
void _remote_object_selected(ObjectID p_object);
|
||||
void _remote_object_edited(ObjectID, const String &p_prop, const Variant &p_value);
|
||||
void _remote_object_property_updated(ObjectID p_id, const String &p_property);
|
||||
|
||||
void _video_mem_request();
|
||||
|
||||
|
@ -221,28 +189,34 @@ private:
|
|||
|
||||
void _network_profiler_activate(bool p_enable);
|
||||
|
||||
void _paused();
|
||||
|
||||
void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj);
|
||||
void _clear_remote_objects();
|
||||
void _clear_errors_list();
|
||||
|
||||
void _error_tree_item_rmb_selected(const Vector2 &p_pos);
|
||||
void _item_menu_id_pressed(int p_option);
|
||||
void _tab_changed(int p_tab);
|
||||
|
||||
void _put_msg(String p_message, Array p_data);
|
||||
void _export_csv();
|
||||
|
||||
void _clear_execution();
|
||||
void _stop_and_notify();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void start();
|
||||
void pause();
|
||||
void unpause();
|
||||
void request_remote_object(ObjectID p_obj_id);
|
||||
void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value);
|
||||
Object *get_remote_object(ObjectID p_id);
|
||||
|
||||
// Needed by _live_edit_set, buttons state.
|
||||
void set_editor_remote_tree(const Tree *p_tree) { editor_remote_tree = p_tree; }
|
||||
|
||||
void request_remote_tree();
|
||||
const SceneDebuggerTree *get_remote_tree();
|
||||
|
||||
void start(Ref<StreamPeerTCP> p_connection);
|
||||
void stop();
|
||||
|
||||
void debug_skip_breakpoints();
|
||||
|
@ -252,14 +226,23 @@ public:
|
|||
void debug_step();
|
||||
void debug_break();
|
||||
void debug_continue();
|
||||
bool is_breaked() const { return breaked; }
|
||||
bool is_debuggable() const { return can_debug; }
|
||||
bool is_session_active() { return connection.is_valid() && connection->is_connected_to_host(); };
|
||||
int get_remote_pid() const { return remote_pid; }
|
||||
|
||||
int get_error_count() const { return error_count; }
|
||||
int get_warning_count() const { return warning_count; }
|
||||
String get_stack_script_file() const;
|
||||
int get_stack_script_line() const;
|
||||
int get_stack_script_frame() const;
|
||||
|
||||
void update_tabs();
|
||||
String get_var_value(const String &p_var) const;
|
||||
|
||||
void save_node(ObjectID p_id, const String &p_file);
|
||||
void set_live_debugging(bool p_enable);
|
||||
|
||||
static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE);
|
||||
static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
|
||||
|
||||
void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name);
|
||||
void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name);
|
||||
void live_debug_remove_node(const NodePath &p_at);
|
||||
|
@ -275,15 +258,6 @@ public:
|
|||
|
||||
void update_live_edit_root();
|
||||
|
||||
void set_hide_on_stop(bool p_hide);
|
||||
|
||||
bool get_debug_with_external_editor() const;
|
||||
void set_debug_with_external_editor(bool p_enabled);
|
||||
|
||||
Ref<Script> get_dump_stack_script() const;
|
||||
|
||||
void set_tool_button(Button *p_tb) { debugger_button = p_tb; }
|
||||
|
||||
void reload_scripts();
|
||||
|
||||
bool is_skip_breakpoints();
|
|
@ -123,6 +123,7 @@
|
|||
#include "editor/plugins/cpu_particles_2d_editor_plugin.h"
|
||||
#include "editor/plugins/cpu_particles_editor_plugin.h"
|
||||
#include "editor/plugins/curve_editor_plugin.h"
|
||||
#include "editor/plugins/debugger_editor_plugin.h"
|
||||
#include "editor/plugins/editor_preview_plugins.h"
|
||||
#include "editor/plugins/gi_probe_editor_plugin.h"
|
||||
#include "editor/plugins/gradient_editor_plugin.h"
|
||||
|
@ -168,7 +169,6 @@
|
|||
#include "editor/quick_open.h"
|
||||
#include "editor/register_exporters.h"
|
||||
#include "editor/run_settings_dialog.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "editor/settings_config_dialog.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -472,7 +472,7 @@ void EditorNode::_notification(int p_what) {
|
|||
recent_scenes->set_as_minsize();
|
||||
|
||||
// debugger area
|
||||
if (ScriptEditor::get_singleton()->get_debugger()->is_visible())
|
||||
if (EditorDebuggerNode::get_singleton()->is_visible())
|
||||
bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles"));
|
||||
|
||||
// update_icons
|
||||
|
@ -1846,7 +1846,7 @@ void EditorNode::_edit_current() {
|
|||
|
||||
Node *selected_node = NULL;
|
||||
|
||||
if (current_obj->is_class("ScriptEditorDebuggerInspectedObject")) {
|
||||
if (current_obj->is_class("EditorDebuggerRemoteObject")) {
|
||||
editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow.");
|
||||
capitalize = false;
|
||||
disable_folding = true;
|
||||
|
@ -2048,9 +2048,13 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
|
|||
editor_data.get_editor_breakpoints(&breakpoints);
|
||||
|
||||
args = ProjectSettings::get_singleton()->get("editor/main_run_args");
|
||||
skip_breakpoints = ScriptEditor::get_singleton()->get_debugger()->is_skip_breakpoints();
|
||||
skip_breakpoints = EditorDebuggerNode::get_singleton()->is_skip_breakpoints();
|
||||
|
||||
Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints);
|
||||
int instances = 1;
|
||||
if (debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO)))
|
||||
instances = 2;
|
||||
|
||||
Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints, instances);
|
||||
|
||||
if (error != OK) {
|
||||
|
||||
|
@ -2481,6 +2485,16 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
|
|||
|
||||
run_settings_dialog->popup_run_settings();
|
||||
} break;
|
||||
case RUN_DEBUG_ONE: {
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_ONE), true);
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO), false);
|
||||
|
||||
} break;
|
||||
case RUN_DEBUG_TWO: {
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO), true);
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_ONE), false);
|
||||
|
||||
} break;
|
||||
case RUN_SETTINGS: {
|
||||
|
||||
project_settings->popup_project_settings();
|
||||
|
@ -2571,7 +2585,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
|
|||
bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG));
|
||||
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG), !ischecked);
|
||||
ScriptEditor::get_singleton()->get_debugger()->set_live_debugging(!ischecked);
|
||||
EditorDebuggerNode::get_singleton()->set_live_debugging(!ischecked);
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked);
|
||||
|
||||
} break;
|
||||
|
@ -3242,7 +3256,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
|
|||
|
||||
//this should only happen at the very end
|
||||
|
||||
ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
|
||||
EditorDebuggerNode::get_singleton()->update_live_edit_root();
|
||||
ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene()));
|
||||
editor_data.notify_edited_scene_changed();
|
||||
}
|
||||
|
@ -3477,7 +3491,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
|
|||
opening_prev = false;
|
||||
scene_tree_dock->set_selected(new_scene);
|
||||
|
||||
ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
|
||||
EditorDebuggerNode::get_singleton()->update_live_edit_root();
|
||||
|
||||
push_item(new_scene);
|
||||
|
||||
|
@ -3619,7 +3633,7 @@ void EditorNode::_quick_run() {
|
|||
_run(false, quick_run->get_selected());
|
||||
}
|
||||
|
||||
void EditorNode::notify_child_process_exited() {
|
||||
void EditorNode::notify_all_debug_sessions_exited() {
|
||||
|
||||
_menu_option_confirm(RUN_STOP, false);
|
||||
stop_button->set_pressed(false);
|
||||
|
@ -3703,9 +3717,13 @@ void EditorNode::unregister_editor_types() {
|
|||
_init_callbacks.clear();
|
||||
}
|
||||
|
||||
void EditorNode::stop_child_process() {
|
||||
void EditorNode::stop_child_process(OS::ProcessID p_pid) {
|
||||
|
||||
_menu_option_confirm(RUN_STOP, false);
|
||||
if (has_child_process(p_pid)) {
|
||||
editor_run.stop_child_process(p_pid);
|
||||
if (!editor_run.get_child_process_count()) // All children stopped. Closing.
|
||||
_menu_option_confirm(RUN_STOP, false);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
|
||||
|
@ -4888,7 +4906,7 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
|
|||
bottom_panel_items[i].button->set_pressed(i == p_idx);
|
||||
bottom_panel_items[i].control->set_visible(i == p_idx);
|
||||
}
|
||||
if (ScriptEditor::get_singleton()->get_debugger() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller
|
||||
if (EditorDebuggerNode::get_singleton() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller
|
||||
bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles"));
|
||||
} else {
|
||||
bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
|
||||
|
@ -6254,6 +6272,13 @@ EditorNode::EditorNode() {
|
|||
p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS);
|
||||
p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
|
||||
p->set_item_checked(p->get_item_count() - 1, true);
|
||||
|
||||
// Multi-instance, start/stop
|
||||
p->add_separator();
|
||||
p->add_radio_check_item(TTR("Debug 1 instance"), RUN_DEBUG_ONE);
|
||||
p->add_radio_check_item(TTR("Debug 2 instances"), RUN_DEBUG_TWO);
|
||||
p->set_item_checked(p->get_item_index(RUN_DEBUG_ONE), true);
|
||||
|
||||
p->connect_compat("id_pressed", this, "_menu_option");
|
||||
|
||||
menu_hb->add_spacer();
|
||||
|
@ -6639,6 +6664,7 @@ EditorNode::EditorNode() {
|
|||
|
||||
file_server = memnew(EditorFileServer);
|
||||
|
||||
add_editor_plugin(memnew(DebuggerEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(AnimationPlayerEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(CanvasItemEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(SpatialEditorPlugin(this)));
|
||||
|
|
|
@ -162,6 +162,8 @@ private:
|
|||
RUN_PLAY_NATIVE,
|
||||
RUN_PLAY_CUSTOM_SCENE,
|
||||
RUN_SCENE_SETTINGS,
|
||||
RUN_DEBUG_ONE,
|
||||
RUN_DEBUG_TWO,
|
||||
RUN_SETTINGS,
|
||||
RUN_PROJECT_DATA_FOLDER,
|
||||
RUN_PROJECT_MANAGER,
|
||||
|
@ -764,10 +766,10 @@ public:
|
|||
|
||||
void set_convert_old_scene(bool p_old) { convert_old = p_old; }
|
||||
|
||||
void notify_child_process_exited();
|
||||
void notify_all_debug_sessions_exited();
|
||||
|
||||
OS::ProcessID get_child_process_id() const { return editor_run.get_pid(); }
|
||||
void stop_child_process();
|
||||
OS::ProcessID has_child_process(OS::ProcessID p_pid) const { return editor_run.has_child_process(p_pid); }
|
||||
void stop_child_process(OS::ProcessID p_pid);
|
||||
|
||||
Ref<Theme> get_editor_theme() const { return theme; }
|
||||
Ref<Script> get_object_custom_type_base(const Object *p_object) const;
|
||||
|
|
|
@ -106,7 +106,7 @@ void EditorPath::update_path() {
|
|||
|
||||
if (name == "")
|
||||
name = r->get_class();
|
||||
} else if (obj->is_class("ScriptEditorDebuggerInspectedObject"))
|
||||
} else if (obj->is_class("EditorDebuggerRemoteObject"))
|
||||
name = obj->call("get_title");
|
||||
else if (Object::cast_to<Node>(obj))
|
||||
name = Object::cast_to<Node>(obj)->get_name();
|
||||
|
|
|
@ -38,7 +38,7 @@ EditorRun::Status EditorRun::get_status() const {
|
|||
return status;
|
||||
}
|
||||
|
||||
Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints) {
|
||||
Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints, const int &p_instances) {
|
||||
|
||||
List<String> args;
|
||||
|
||||
|
@ -187,20 +187,40 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
|
|||
};
|
||||
printf("\n");
|
||||
|
||||
pid = 0;
|
||||
Error err = OS::get_singleton()->execute(exec, args, false, &pid);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
for (int i = 0; i < p_instances; i++) {
|
||||
OS::ProcessID pid = 0;
|
||||
Error err = OS::get_singleton()->execute(exec, args, false, &pid);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
pids.push_back(pid);
|
||||
}
|
||||
|
||||
status = STATUS_PLAY;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool EditorRun::has_child_process(OS::ProcessID p_pid) const {
|
||||
for (const List<OS::ProcessID>::Element *E = pids.front(); E; E = E->next()) {
|
||||
if (E->get() == p_pid)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorRun::stop_child_process(OS::ProcessID p_pid) {
|
||||
if (has_child_process(p_pid)) {
|
||||
OS::get_singleton()->kill(p_pid);
|
||||
pids.erase(p_pid);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorRun::stop() {
|
||||
|
||||
if (status != STATUS_STOP && pid != 0) {
|
||||
if (status != STATUS_STOP && pids.size() > 0) {
|
||||
|
||||
OS::get_singleton()->kill(pid);
|
||||
for (List<OS::ProcessID>::Element *E = pids.front(); E; E = E->next()) {
|
||||
OS::get_singleton()->kill(E->get());
|
||||
}
|
||||
}
|
||||
|
||||
status = STATUS_STOP;
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
STATUS_STOP
|
||||
};
|
||||
|
||||
OS::ProcessID pid;
|
||||
List<OS::ProcessID> pids;
|
||||
|
||||
private:
|
||||
bool debug_collisions;
|
||||
|
@ -51,11 +51,13 @@ private:
|
|||
|
||||
public:
|
||||
Status get_status() const;
|
||||
Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false);
|
||||
Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false, const int &p_instances = 1);
|
||||
void run_native_notify() { status = STATUS_PLAY; }
|
||||
void stop();
|
||||
|
||||
OS::ProcessID get_pid() const { return pid; }
|
||||
void stop_child_process(OS::ProcessID p_pid);
|
||||
bool has_child_process(OS::ProcessID p_pid) const;
|
||||
int get_child_process_count() const { return pids.size(); }
|
||||
|
||||
void set_debug_collisions(bool p_debug);
|
||||
bool get_debug_collisions() const;
|
||||
|
|
|
@ -247,7 +247,7 @@ void InspectorDock::_prepare_history() {
|
|||
}
|
||||
} else if (Object::cast_to<Node>(obj)) {
|
||||
text = Object::cast_to<Node>(obj)->get_name();
|
||||
} else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) {
|
||||
} else if (obj->is_class("EditorDebuggerRemoteObject")) {
|
||||
text = obj->call("get_title");
|
||||
} else {
|
||||
text = obj->get_class();
|
||||
|
|
|
@ -34,12 +34,12 @@
|
|||
#include "core/os/keyboard.h"
|
||||
#include "core/print_string.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "scene/2d/light_2d.h"
|
||||
#include "scene/2d/particles_2d.h"
|
||||
#include "scene/2d/polygon_2d.h"
|
||||
|
@ -3990,7 +3990,7 @@ void CanvasItemEditor::_notification(int p_what) {
|
|||
|
||||
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
|
||||
if (!is_visible() && override_camera_button->is_pressed()) {
|
||||
ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
|
||||
override_camera_button->set_pressed(false);
|
||||
|
@ -4345,7 +4345,7 @@ void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) {
|
|||
viewport->update();
|
||||
}
|
||||
void CanvasItemEditor::_button_override_camera(bool p_pressed) {
|
||||
ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
if (p_pressed) {
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_2D);
|
||||
|
@ -5960,9 +5960,9 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
|
|||
|
||||
if (parent) {
|
||||
String new_name = parent->validate_child_name(child);
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
|
||||
}
|
||||
|
||||
// handle with different property for texture
|
||||
|
@ -6030,9 +6030,9 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
|
|||
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
|
||||
|
||||
String new_name = parent->validate_child_name(instanced_scene);
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
|
||||
|
||||
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
|
||||
if (parent_ci) {
|
||||
|
|
51
editor/plugins/debugger_editor_plugin.cpp
Normal file
51
editor/plugins/debugger_editor_plugin.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*************************************************************************/
|
||||
/* debugger_editor_plugin.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "debugger_editor_plugin.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
|
||||
DebuggerEditorPlugin::DebuggerEditorPlugin(EditorNode *p_editor) {
|
||||
ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11);
|
||||
ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10);
|
||||
ED_SHORTCUT("debugger/break", TTR("Break"));
|
||||
ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12);
|
||||
ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open"));
|
||||
ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor"));
|
||||
|
||||
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
|
||||
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
|
||||
debugger->set_tool_button(db);
|
||||
}
|
||||
|
||||
DebuggerEditorPlugin::~DebuggerEditorPlugin() {
|
||||
// Should delete debugger?
|
||||
}
|
50
editor/plugins/debugger_editor_plugin.h
Normal file
50
editor/plugins/debugger_editor_plugin.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*************************************************************************/
|
||||
/* debugger_editor_plugin.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 DEBUGGER_EDITOR_PLUGIN_H
|
||||
#define DEBUGGER_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
|
||||
class DebuggerEditorPlugin : public EditorPlugin {
|
||||
|
||||
GDCLASS(DebuggerEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
virtual String get_name() const { return "Debugger"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
|
||||
DebuggerEditorPlugin(EditorNode *p_node);
|
||||
~DebuggerEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // DEBUGGER_EDITOR_PLUGIN_H
|
|
@ -36,6 +36,8 @@
|
|||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_run_script.h"
|
||||
#include "editor/editor_scale.h"
|
||||
|
@ -44,7 +46,6 @@
|
|||
#include "editor/find_in_files.h"
|
||||
#include "editor/node_dock.h"
|
||||
#include "editor/plugins/shader_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "script_text_editor.h"
|
||||
|
@ -261,7 +262,7 @@ ScriptEditor *ScriptEditor::script_editor = NULL;
|
|||
|
||||
String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) {
|
||||
|
||||
String val = debugger->get_var_value(p_text);
|
||||
String val = EditorDebuggerNode::get_singleton()->get_var_value(p_text);
|
||||
if (val != String()) {
|
||||
return p_text + ": " + val;
|
||||
} else {
|
||||
|
@ -276,11 +277,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) {
|
|||
return;
|
||||
}
|
||||
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug));
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug));
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked);
|
||||
|
||||
for (int i = 0; i < tab_container->get_child_count(); i++) {
|
||||
|
||||
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
|
||||
|
@ -292,11 +288,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) {
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptEditor::_show_debugger(bool p_show) {
|
||||
|
||||
//debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show);
|
||||
}
|
||||
|
||||
void ScriptEditor::_script_created(Ref<Script> p_script) {
|
||||
editor->push_item(p_script.operator->());
|
||||
}
|
||||
|
@ -843,7 +834,7 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
|
|||
|
||||
void ScriptEditor::_live_auto_reload_running_scripts() {
|
||||
pending_auto_reload = false;
|
||||
debugger->reload_scripts();
|
||||
EditorDebuggerNode::get_singleton()->reload_scripts();
|
||||
}
|
||||
|
||||
bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) {
|
||||
|
@ -1123,27 +1114,6 @@ void ScriptEditor::_menu_option(int p_option) {
|
|||
_sort_list_on_update = true;
|
||||
_update_script_names();
|
||||
} break;
|
||||
case DEBUG_SHOW: {
|
||||
if (debugger) {
|
||||
bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW));
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
|
||||
if (visible)
|
||||
debugger->hide();
|
||||
else
|
||||
debugger->show();
|
||||
}
|
||||
} break;
|
||||
case DEBUG_SHOW_KEEP_OPEN: {
|
||||
bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN));
|
||||
if (debugger)
|
||||
debugger->set_hide_on_stop(visible);
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
|
||||
} break;
|
||||
case DEBUG_WITH_EXTERNAL_EDITOR: {
|
||||
bool debug_with_external_editor = !debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));
|
||||
debugger->set_debug_with_external_editor(debug_with_external_editor);
|
||||
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), debug_with_external_editor);
|
||||
} break;
|
||||
case TOGGLE_SCRIPTS_PANEL: {
|
||||
if (current) {
|
||||
ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current);
|
||||
|
@ -1294,29 +1264,6 @@ void ScriptEditor::_menu_option(int p_option) {
|
|||
case CLOSE_ALL: {
|
||||
_close_all_tabs();
|
||||
} break;
|
||||
case DEBUG_NEXT: {
|
||||
|
||||
if (debugger)
|
||||
debugger->debug_next();
|
||||
} break;
|
||||
case DEBUG_STEP: {
|
||||
|
||||
if (debugger)
|
||||
debugger->debug_step();
|
||||
|
||||
} break;
|
||||
case DEBUG_BREAK: {
|
||||
|
||||
if (debugger)
|
||||
debugger->debug_break();
|
||||
|
||||
} break;
|
||||
case DEBUG_CONTINUE: {
|
||||
|
||||
if (debugger)
|
||||
debugger->debug_continue();
|
||||
|
||||
} break;
|
||||
case WINDOW_MOVE_UP: {
|
||||
|
||||
if (tab_container->get_current_tab() > 0) {
|
||||
|
@ -1439,8 +1386,6 @@ void ScriptEditor::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
|
||||
editor->connect_compat("play_pressed", this, "_editor_play");
|
||||
editor->connect_compat("pause_pressed", this, "_editor_pause");
|
||||
editor->connect_compat("stop_pressed", this, "_editor_stop");
|
||||
editor->connect_compat("script_add_function_request", this, "_add_callback");
|
||||
editor->connect_compat("resource_saved", this, "_res_saved_callback");
|
||||
|
@ -1481,8 +1426,6 @@ void ScriptEditor::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
|
||||
editor->disconnect_compat("play_pressed", this, "_editor_play");
|
||||
editor->disconnect_compat("pause_pressed", this, "_editor_pause");
|
||||
editor->disconnect_compat("stop_pressed", this, "_editor_stop");
|
||||
} break;
|
||||
|
||||
|
@ -2062,7 +2005,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) &&
|
||||
if ((EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) &&
|
||||
p_resource->get_path().is_resource_file() &&
|
||||
p_resource->get_class_name() != StringName("VisualScript") &&
|
||||
bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
|
||||
|
@ -2277,26 +2220,7 @@ void ScriptEditor::open_script_create_dialog(const String &p_base_name, const St
|
|||
script_create_dialog->config(p_base_name, p_base_path);
|
||||
}
|
||||
|
||||
void ScriptEditor::_editor_play() {
|
||||
|
||||
debugger->start();
|
||||
debug_menu->get_popup()->grab_focus();
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
|
||||
}
|
||||
|
||||
void ScriptEditor::_editor_pause() {
|
||||
}
|
||||
void ScriptEditor::_editor_stop() {
|
||||
|
||||
debugger->stop();
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
|
||||
|
||||
for (int i = 0; i < tab_container->get_child_count(); i++) {
|
||||
|
||||
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
|
||||
|
@ -3125,8 +3049,6 @@ void ScriptEditor::_bind_methods() {
|
|||
ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs);
|
||||
ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script);
|
||||
ClassDB::bind_method("_theme_option", &ScriptEditor::_theme_option);
|
||||
ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play);
|
||||
ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause);
|
||||
ClassDB::bind_method("_editor_stop", &ScriptEditor::_editor_stop);
|
||||
ClassDB::bind_method("_add_callback", &ScriptEditor::_add_callback);
|
||||
ClassDB::bind_method("_reload_scripts", &ScriptEditor::_reload_scripts);
|
||||
|
@ -3141,7 +3063,6 @@ void ScriptEditor::_bind_methods() {
|
|||
ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
|
||||
|
||||
ClassDB::bind_method("_breaked", &ScriptEditor::_breaked);
|
||||
ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger);
|
||||
ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip);
|
||||
ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts);
|
||||
ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer);
|
||||
|
@ -3358,26 +3279,16 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
|
|||
script_search_menu->get_popup()->set_hide_on_window_lose_focus(true);
|
||||
script_search_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option");
|
||||
|
||||
debug_menu = memnew(MenuButton);
|
||||
MenuButton *debug_menu = memnew(MenuButton);
|
||||
menu_hb->add_child(debug_menu);
|
||||
debug_menu->set_text(TTR("Debug"));
|
||||
debug_menu->set_switch_on_hover(true);
|
||||
debug_menu->get_popup()->set_hide_on_window_lose_focus(true);
|
||||
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP);
|
||||
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT);
|
||||
debug_menu->get_popup()->add_separator();
|
||||
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK);
|
||||
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12), DEBUG_CONTINUE);
|
||||
debug_menu->get_popup()->add_separator();
|
||||
//debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW);
|
||||
debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN);
|
||||
debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR);
|
||||
debug_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option");
|
||||
debug_menu->hide(); // Handled by EditorDebuggerNode below.
|
||||
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
|
||||
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
|
||||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
debugger->set_script_debug_button(debug_menu);
|
||||
debugger->connect_compat("goto_script_line", this, "_goto_script_line");
|
||||
debugger->connect_compat("set_execution", this, "_set_execution");
|
||||
debugger->connect_compat("clear_execution", this, "_clear_execution");
|
||||
debugger->connect_compat("breaked", this, "_breaked");
|
||||
|
||||
menu_hb->add_spacer();
|
||||
|
||||
|
@ -3445,12 +3356,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
|
|||
error_dialog = memnew(AcceptDialog);
|
||||
add_child(error_dialog);
|
||||
|
||||
debugger = memnew(ScriptEditorDebugger(editor));
|
||||
debugger->connect_compat("goto_script_line", this, "_goto_script_line");
|
||||
debugger->connect_compat("set_execution", this, "_set_execution");
|
||||
debugger->connect_compat("clear_execution", this, "_clear_execution");
|
||||
debugger->connect_compat("show_debugger", this, "_show_debugger");
|
||||
|
||||
disk_changed = memnew(ConfirmationDialog);
|
||||
{
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
|
@ -3475,11 +3380,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
|
|||
|
||||
script_editor = this;
|
||||
|
||||
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
|
||||
debugger->set_tool_button(db);
|
||||
|
||||
debugger->connect_compat("breaked", this, "_breaked");
|
||||
|
||||
autosave_timer = memnew(Timer);
|
||||
autosave_timer->set_one_shot(false);
|
||||
autosave_timer->connect_compat(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer");
|
||||
|
@ -3505,7 +3405,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
|
|||
find_in_files_button->hide();
|
||||
|
||||
history_pos = -1;
|
||||
//debugger_gui->hide();
|
||||
|
||||
edit_pass = 0;
|
||||
trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
|
||||
|
|
|
@ -73,7 +73,7 @@ public:
|
|||
ScriptEditorQuickOpen();
|
||||
};
|
||||
|
||||
class ScriptEditorDebugger;
|
||||
class EditorDebuggerNode;
|
||||
|
||||
class ScriptEditorBase : public VBoxContainer {
|
||||
|
||||
|
@ -155,13 +155,6 @@ class ScriptEditor : public PanelContainer {
|
|||
FILE_COPY_PATH,
|
||||
FILE_TOOL_RELOAD,
|
||||
FILE_TOOL_RELOAD_SOFT,
|
||||
DEBUG_NEXT,
|
||||
DEBUG_STEP,
|
||||
DEBUG_BREAK,
|
||||
DEBUG_CONTINUE,
|
||||
DEBUG_SHOW,
|
||||
DEBUG_SHOW_KEEP_OPEN,
|
||||
DEBUG_WITH_EXTERNAL_EDITOR,
|
||||
SEARCH_IN_FILES,
|
||||
REPLACE_IN_FILES,
|
||||
SEARCH_HELP,
|
||||
|
@ -233,7 +226,6 @@ class ScriptEditor : public PanelContainer {
|
|||
AcceptDialog *error_dialog;
|
||||
ConfirmationDialog *erase_tab_confirm;
|
||||
ScriptCreateDialog *script_create_dialog;
|
||||
ScriptEditorDebugger *debugger;
|
||||
ToolButton *scripts_visible;
|
||||
|
||||
String current_theme;
|
||||
|
@ -315,8 +307,6 @@ class ScriptEditor : public PanelContainer {
|
|||
|
||||
EditorScriptCodeCompletionCache *completion_cache;
|
||||
|
||||
void _editor_play();
|
||||
void _editor_pause();
|
||||
void _editor_stop();
|
||||
|
||||
int edit_pass;
|
||||
|
@ -335,7 +325,6 @@ class ScriptEditor : public PanelContainer {
|
|||
void _set_execution(REF p_script, int p_line);
|
||||
void _clear_execution(REF p_script);
|
||||
void _breaked(bool p_breaked, bool p_can_debug);
|
||||
void _show_debugger(bool p_show);
|
||||
void _update_window_menu();
|
||||
void _script_created(Ref<Script> p_script);
|
||||
|
||||
|
@ -457,7 +446,6 @@ public:
|
|||
|
||||
VSplitContainer *get_left_list_split() { return list_split; }
|
||||
|
||||
ScriptEditorDebugger *get_debugger() { return debugger; }
|
||||
void set_live_auto_reload_running_scripts(bool p_enabled);
|
||||
|
||||
static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func);
|
||||
|
|
|
@ -32,10 +32,10 @@
|
|||
|
||||
#include "core/math/expression.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
|
||||
void ConnectionInfoDialog::ok_pressed() {
|
||||
}
|
||||
|
@ -870,7 +870,7 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) {
|
|||
|
||||
void ScriptTextEditor::_breakpoint_toggled(int p_row) {
|
||||
|
||||
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
|
||||
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
|
||||
}
|
||||
|
||||
void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) {
|
||||
|
@ -1294,7 +1294,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
|||
int line = tx->cursor_get_line();
|
||||
bool dobreak = !tx->is_line_set_as_breakpoint(line);
|
||||
tx->set_line_as_breakpoint(line, dobreak);
|
||||
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak);
|
||||
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
|
||||
} break;
|
||||
case DEBUG_REMOVE_ALL_BREAKPOINTS: {
|
||||
|
||||
|
@ -1305,7 +1305,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
|||
int line = E->get();
|
||||
bool dobreak = !tx->is_line_set_as_breakpoint(line);
|
||||
tx->set_line_as_breakpoint(line, dobreak);
|
||||
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak);
|
||||
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
|
||||
}
|
||||
} break;
|
||||
case DEBUG_GOTO_NEXT_BREAKPOINT: {
|
||||
|
|
|
@ -36,12 +36,12 @@
|
|||
#include "core/print_string.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/sort_array.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "editor/spatial_editor_gizmos.h"
|
||||
#include "scene/3d/camera.h"
|
||||
#include "scene/3d/collision_shape.h"
|
||||
|
@ -3421,9 +3421,9 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
|
|||
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
|
||||
|
||||
String new_name = parent->validate_child_name(instanced_scene);
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
|
||||
|
||||
Transform global_transform;
|
||||
Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
|
||||
|
@ -4497,7 +4497,7 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) {
|
|||
} break;
|
||||
|
||||
case MENU_TOOL_OVERRIDE_CAMERA: {
|
||||
ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
if (pressed) {
|
||||
using Override = ScriptEditorDebugger::CameraOverride;
|
||||
|
@ -4554,7 +4554,7 @@ void SpatialEditor::_update_camera_override_viewport(Object *p_viewport) {
|
|||
if (!current_viewport)
|
||||
return;
|
||||
|
||||
ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
camera_override_viewport_id = current_viewport->index;
|
||||
if (debugger->get_camera_override() >= ScriptEditorDebugger::OVERRIDE_3D_1) {
|
||||
|
@ -5513,7 +5513,7 @@ void SpatialEditor::_notification(int p_what) {
|
|||
_init_grid();
|
||||
} else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
|
||||
if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) {
|
||||
ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
|
||||
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "core/os/keyboard.h"
|
||||
#include "core/project_settings.h"
|
||||
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_feature_profile.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
|
@ -44,7 +45,6 @@
|
|||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/plugins/spatial_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
|
@ -229,9 +229,9 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node
|
|||
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
|
||||
|
||||
String new_name = parent->validate_child_name(instanced_scene);
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name)));
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name)));
|
||||
}
|
||||
|
||||
editor_data->get_undo_redo().commit_action();
|
||||
|
@ -591,10 +591,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
|||
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", dup);
|
||||
editor_data->get_undo_redo().add_do_reference(dup);
|
||||
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name());
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name())));
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name());
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name())));
|
||||
}
|
||||
|
||||
editor_data->get_undo_redo().commit_action();
|
||||
|
@ -1584,7 +1584,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
|||
if (p_position_in_parent >= 0)
|
||||
editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc);
|
||||
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
String old_name = former_names[ni];
|
||||
String new_name = new_parent->validate_child_name(node);
|
||||
|
||||
|
@ -1609,8 +1609,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
|||
path_renames[ni].second = fixed_node_path;
|
||||
}
|
||||
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc);
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc);
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
|
||||
|
||||
if (p_keep_global_xform) {
|
||||
if (Object::cast_to<Node2D>(node))
|
||||
|
@ -1849,9 +1849,9 @@ void SceneTreeDock::_delete_confirm() {
|
|||
editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners);
|
||||
editor_data->get_undo_redo().add_undo_reference(n);
|
||||
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id());
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index());
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id());
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index());
|
||||
}
|
||||
}
|
||||
editor_data->get_undo_redo().commit_action();
|
||||
|
@ -1950,9 +1950,9 @@ void SceneTreeDock::_do_create(Node *p_parent) {
|
|||
editor_data->get_undo_redo().add_undo_method(p_parent, "remove_child", child);
|
||||
|
||||
String new_name = p_parent->validate_child_name(child);
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name)));
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name);
|
||||
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name)));
|
||||
|
||||
} else {
|
||||
|
||||
|
|
|
@ -32,13 +32,13 @@
|
|||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor_file_system.h"
|
||||
#include "editor_log.h"
|
||||
#include "editor_node.h"
|
||||
#include "editor_scale.h"
|
||||
#include "editor_settings.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "script_editor_debugger.h"
|
||||
|
||||
void EditorSettingsDialog::ok_pressed() {
|
||||
|
||||
|
@ -119,9 +119,8 @@ void EditorSettingsDialog::_notification(int p_what) {
|
|||
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
undo_redo->set_method_notify_callback(sed->_method_changeds, sed);
|
||||
undo_redo->set_property_notify_callback(sed->_property_changeds, sed);
|
||||
undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, NULL);
|
||||
undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, NULL);
|
||||
undo_redo->set_commit_notify_callback(_undo_redo_callback, this);
|
||||
} break;
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "core/project_settings.h"
|
||||
#include "core/register_core_types.h"
|
||||
#include "core/script_debugger_local.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/translation.h"
|
||||
#include "core/version.h"
|
||||
|
@ -58,7 +59,6 @@
|
|||
#include "main/tests/test_main.h"
|
||||
#include "modules/register_module_types.h"
|
||||
#include "platform/register_platform_apis.h"
|
||||
#include "scene/debugger/script_debugger_remote.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/register_scene_types.h"
|
||||
|
@ -1657,12 +1657,6 @@ bool Main::start() {
|
|||
|
||||
if (!project_manager && !editor) { // game
|
||||
if (game_path != "" || script != "") {
|
||||
if (script_debugger && script_debugger->is_remote()) {
|
||||
ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(script_debugger);
|
||||
|
||||
remote_debugger->set_scene_tree(sml);
|
||||
}
|
||||
|
||||
//autoload
|
||||
List<PropertyInfo> props;
|
||||
ProjectSettings::get_singleton()->get_property_list(&props);
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace GodotTools
|
|||
return;
|
||||
|
||||
// Notify running game for hot-reload
|
||||
Internal.ScriptEditorDebuggerReloadScripts();
|
||||
Internal.EditorDebuggerNodeReloadScripts();
|
||||
|
||||
// Hot-reload in the editor
|
||||
GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace GodotTools.Internals
|
|||
|
||||
public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
|
||||
|
||||
public static void ScriptEditorDebuggerReloadScripts() => internal_ScriptEditorDebuggerReloadScripts();
|
||||
public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
|
||||
|
||||
public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
|
||||
internal_ScriptEditorEdit(resource, line, col, grabFocus);
|
||||
|
@ -88,7 +88,7 @@ namespace GodotTools.Internals
|
|||
private static extern void internal_ReloadAssemblies(bool softReload);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_ScriptEditorDebuggerReloadScripts();
|
||||
private static extern void internal_EditorDebuggerNodeReloadScripts();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
|
||||
|
|
|
@ -36,10 +36,10 @@
|
|||
|
||||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
|
@ -305,8 +305,8 @@ void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void godot_icall_Internal_ScriptEditorDebuggerReloadScripts() {
|
||||
ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
|
||||
void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
|
||||
EditorDebuggerNode::get_singleton()->reload_scripts();
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
|
||||
|
@ -348,9 +348,9 @@ void godot_icall_Internal_EditorRunStop() {
|
|||
}
|
||||
|
||||
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
|
||||
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
|
||||
if (sed) {
|
||||
sed->reload_scripts();
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
if (ed) {
|
||||
ed->reload_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,7 @@ void register_editor_internal_calls() {
|
|||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebuggerReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebuggerReloadScripts);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#include "core/reference.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/script_editor_debugger.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#endif
|
||||
|
||||
#include "../csharp_script.h"
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "os_windows.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/version_generated.gen.h"
|
||||
|
||||
#if defined(OPENGL_ENABLED)
|
||||
|
|
867
scene/debugger/scene_debugger.cpp
Normal file
867
scene/debugger/scene_debugger.cpp
Normal file
|
@ -0,0 +1,867 @@
|
|||
/*************************************************************************/
|
||||
/* scene_debugger.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "scene_debugger.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
void SceneDebugger::initialize() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
LiveEditor::singleton = memnew(LiveEditor);
|
||||
ScriptDebuggerRemote::scene_tree_parse_func = SceneDebugger::parse_message;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneDebugger::deinitialize() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (LiveEditor::singleton) {
|
||||
memdelete(LiveEditor::singleton);
|
||||
LiveEditor::singleton = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return ERR_UNCONFIGURED;
|
||||
LiveEditor *live_editor = LiveEditor::get_singleton();
|
||||
if (!live_editor)
|
||||
return ERR_UNCONFIGURED;
|
||||
if (p_msg == "request_scene_tree") { // Scene tree
|
||||
live_editor->_send_tree();
|
||||
|
||||
} else if (p_msg == "save_node") { // Save node.
|
||||
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
|
||||
_save_node(p_args[0], p_args[1]);
|
||||
|
||||
} else if (p_msg == "inspect_object") { // Object Inspect
|
||||
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
|
||||
ObjectID id = p_args[0];
|
||||
_send_object_id(id);
|
||||
|
||||
} else if (p_msg == "override_camera_2D:set") { // Camera
|
||||
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
|
||||
bool enforce = p_args[0];
|
||||
scene_tree->get_root()->enable_canvas_transform_override(enforce);
|
||||
|
||||
} else if (p_msg == "override_camera_2D:transform") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
|
||||
Transform2D transform = p_args[1];
|
||||
scene_tree->get_root()->set_canvas_transform_override(transform);
|
||||
|
||||
} else if (p_msg == "override_camera_3D:set") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
|
||||
bool enable = p_args[0];
|
||||
scene_tree->get_root()->enable_camera_override(enable);
|
||||
|
||||
} else if (p_msg == "override_camera_3D:transform") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 5, ERR_INVALID_DATA);
|
||||
Transform transform = p_args[0];
|
||||
bool is_perspective = p_args[1];
|
||||
float size_or_fov = p_args[2];
|
||||
float near = p_args[3];
|
||||
float far = p_args[4];
|
||||
if (is_perspective) {
|
||||
scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
|
||||
} else {
|
||||
scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
|
||||
}
|
||||
scene_tree->get_root()->set_camera_override_transform(transform);
|
||||
|
||||
} else if (p_msg == "set_object_property") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
_set_object_property(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (!p_msg.begins_with("live_")) { // Live edits below.
|
||||
return ERR_SKIP;
|
||||
} else if (p_msg == "live_set_root") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
|
||||
live_editor->_root_func(p_args[0], p_args[1]);
|
||||
|
||||
} else if (p_msg == "live_node_path") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
|
||||
live_editor->_node_path_func(p_args[0], p_args[1]);
|
||||
|
||||
} else if (p_msg == "live_res_path") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
|
||||
live_editor->_res_path_func(p_args[0], p_args[1]);
|
||||
|
||||
} else if (p_msg == "live_node_prop_res") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_node_set_res_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_node_prop") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_node_set_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_res_prop_res") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_res_set_res_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_res_prop") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_node_call") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 7, ERR_INVALID_DATA);
|
||||
live_editor->_node_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6]);
|
||||
|
||||
} else if (p_msg == "live_res_call") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 7, ERR_INVALID_DATA);
|
||||
live_editor->_res_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6]);
|
||||
|
||||
} else if (p_msg == "live_create_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_create_node_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_instance_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_instance_node_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_remove_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
|
||||
live_editor->_remove_node_func(p_args[0]);
|
||||
|
||||
} else if (p_msg == "live_remove_and_keep_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
|
||||
live_editor->_remove_and_keep_node_func(p_args[0], p_args[1]);
|
||||
|
||||
} else if (p_msg == "live_restore_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
|
||||
live_editor->_restore_node_func(p_args[0], p_args[1], p_args[2]);
|
||||
|
||||
} else if (p_msg == "live_duplicate_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
|
||||
live_editor->_duplicate_node_func(p_args[0], p_args[1]);
|
||||
|
||||
} else if (p_msg == "live_reparent_node") {
|
||||
ERR_FAIL_COND_V(p_args.size() < 4, ERR_INVALID_DATA);
|
||||
live_editor->_reparent_node_func(p_args[0], p_args[1], p_args[2], p_args[3]);
|
||||
} else {
|
||||
return ERR_SKIP;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void SceneDebugger::_save_node(ObjectID id, const String &p_path) {
|
||||
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
|
||||
ERR_FAIL_COND(!node);
|
||||
|
||||
WARN_PRINT("SAVING " + itos(id) + " TO " + p_path);
|
||||
Ref<PackedScene> ps = memnew(PackedScene);
|
||||
ps->pack(node);
|
||||
ResourceSaver::save(p_path, ps);
|
||||
}
|
||||
|
||||
void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) {
|
||||
SceneDebuggerObject obj(p_id);
|
||||
if (obj.id.is_null())
|
||||
return;
|
||||
|
||||
Array arr;
|
||||
obj.serialize(arr);
|
||||
ScriptDebugger::get_singleton()->send_message("inspect_object", arr);
|
||||
}
|
||||
|
||||
void SceneDebugger::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
|
||||
|
||||
Object *obj = ObjectDB::get_instance(p_id);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
String prop_name = p_property;
|
||||
if (p_property.begins_with("Members/")) {
|
||||
Vector<String> ss = p_property.split("/");
|
||||
prop_name = ss[ss.size() - 1];
|
||||
}
|
||||
|
||||
obj->set(prop_name, p_value);
|
||||
}
|
||||
|
||||
void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) {
|
||||
LiveEditor *debugger = LiveEditor::get_singleton();
|
||||
if (!debugger)
|
||||
return;
|
||||
|
||||
if (ScriptDebugger::get_singleton() && p_filename != String()) {
|
||||
debugger->live_scene_edit_cache[p_filename].insert(p_node);
|
||||
}
|
||||
}
|
||||
void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) {
|
||||
LiveEditor *debugger = LiveEditor::get_singleton();
|
||||
if (!debugger)
|
||||
return;
|
||||
|
||||
Map<String, Set<Node *> > &edit_cache = debugger->live_scene_edit_cache;
|
||||
Map<String, Set<Node *> >::Element *E = edit_cache.find(p_filename);
|
||||
if (E) {
|
||||
E->get().erase(p_node);
|
||||
if (E->get().size() == 0) {
|
||||
edit_cache.erase(E);
|
||||
}
|
||||
}
|
||||
|
||||
Map<Node *, Map<ObjectID, Node *> > &remove_list = debugger->live_edit_remove_list;
|
||||
Map<Node *, Map<ObjectID, Node *> >::Element *F = remove_list.find(p_node);
|
||||
if (F) {
|
||||
for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
|
||||
|
||||
memdelete(G->get());
|
||||
}
|
||||
remove_list.erase(F);
|
||||
}
|
||||
}
|
||||
|
||||
/// SceneDebuggerObject
|
||||
SceneDebuggerObject::SceneDebuggerObject(ObjectID p_id) {
|
||||
id = ObjectID();
|
||||
Object *obj = ObjectDB::get_instance(p_id);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
id = p_id;
|
||||
class_name = obj->get_class();
|
||||
|
||||
if (ScriptInstance *si = obj->get_script_instance()) {
|
||||
// Read script instance constants and variables
|
||||
if (!si->get_script().is_null()) {
|
||||
Script *s = si->get_script().ptr();
|
||||
_parse_script_properties(s, si);
|
||||
}
|
||||
}
|
||||
|
||||
if (Node *node = Object::cast_to<Node>(obj)) {
|
||||
// Add specialized NodePath info (if inside tree).
|
||||
if (node->is_inside_tree()) {
|
||||
PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));
|
||||
properties.push_back(SceneDebuggerProperty(pi, node->get_path()));
|
||||
} else { // Can't ask for path if a node is not in tree.
|
||||
PropertyInfo pi(Variant::STRING, String("Node/path"));
|
||||
properties.push_back(SceneDebuggerProperty(pi, "[Orphan]"));
|
||||
}
|
||||
} else if (Script *s = Object::cast_to<Script>(obj)) {
|
||||
// Add script constants (no instance).
|
||||
_parse_script_properties(s, NULL);
|
||||
}
|
||||
|
||||
// Add base object properties.
|
||||
List<PropertyInfo> pinfo;
|
||||
obj->get_property_list(&pinfo, true);
|
||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
||||
if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
|
||||
properties.push_back(SceneDebuggerProperty(E->get(), obj->get(E->get().name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) {
|
||||
typedef Map<const Script *, Set<StringName> > ScriptMemberMap;
|
||||
typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap;
|
||||
|
||||
ScriptMemberMap members;
|
||||
if (p_instance) {
|
||||
members[p_script] = Set<StringName>();
|
||||
p_script->get_members(&(members[p_script]));
|
||||
}
|
||||
|
||||
ScriptConstantsMap constants;
|
||||
constants[p_script] = Map<StringName, Variant>();
|
||||
p_script->get_constants(&(constants[p_script]));
|
||||
|
||||
Ref<Script> base = p_script->get_base_script();
|
||||
while (base.is_valid()) {
|
||||
if (p_instance) {
|
||||
members[base.ptr()] = Set<StringName>();
|
||||
base->get_members(&(members[base.ptr()]));
|
||||
}
|
||||
|
||||
constants[base.ptr()] = Map<StringName, Variant>();
|
||||
base->get_constants(&(constants[base.ptr()]));
|
||||
|
||||
base = base->get_base_script();
|
||||
}
|
||||
|
||||
// Members
|
||||
for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
|
||||
for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
|
||||
Variant m;
|
||||
if (p_instance->get(E->get(), m)) {
|
||||
String script_path = sm->key() == p_script ? "" : sm->key()->get_path().get_file() + "/";
|
||||
PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
|
||||
properties.push_back(SceneDebuggerProperty(pi, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Constants
|
||||
for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
|
||||
for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
|
||||
String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/";
|
||||
if (E->value().get_type() == Variant::OBJECT) {
|
||||
Variant id = ((Object *)E->value())->get_instance_id();
|
||||
PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
|
||||
properties.push_back(SceneDebuggerProperty(pi, id));
|
||||
} else {
|
||||
PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
|
||||
properties.push_back(SceneDebuggerProperty(pi, E->value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
|
||||
Array send_props;
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
const PropertyInfo &pi = properties[i].first;
|
||||
Variant &var = properties[i].second;
|
||||
|
||||
WeakRef *ref = Object::cast_to<WeakRef>(var);
|
||||
if (ref) {
|
||||
var = ref->get_ref();
|
||||
}
|
||||
|
||||
RES res = var;
|
||||
|
||||
Array prop;
|
||||
prop.push_back(pi.name);
|
||||
prop.push_back(pi.type);
|
||||
|
||||
PropertyHint hint = pi.hint;
|
||||
String hint_string = pi.hint_string;
|
||||
if (!res.is_null()) {
|
||||
var = res->get_path();
|
||||
} else { //only send information that can be sent..
|
||||
int len = 0; //test how big is this to encode
|
||||
encode_variant(var, NULL, len);
|
||||
if (len > p_max_size) { //limit to max size
|
||||
hint = PROPERTY_HINT_OBJECT_TOO_BIG;
|
||||
hint_string = "";
|
||||
var = Variant();
|
||||
}
|
||||
}
|
||||
prop.push_back(hint);
|
||||
prop.push_back(hint_string);
|
||||
prop.push_back(pi.usage);
|
||||
prop.push_back(var);
|
||||
send_props.push_back(prop);
|
||||
}
|
||||
r_arr.push_back(uint64_t(id));
|
||||
r_arr.push_back(class_name);
|
||||
r_arr.push_back(send_props);
|
||||
}
|
||||
|
||||
void SceneDebuggerObject::deserialize(const Array &p_arr) {
|
||||
#define CHECK_TYPE(p_what, p_type) ERR_FAIL_COND(p_what.get_type() != Variant::p_type);
|
||||
ERR_FAIL_COND(p_arr.size() < 3);
|
||||
CHECK_TYPE(p_arr[0], INT);
|
||||
CHECK_TYPE(p_arr[1], STRING);
|
||||
CHECK_TYPE(p_arr[2], ARRAY);
|
||||
|
||||
id = uint64_t(p_arr[0]);
|
||||
class_name = p_arr[1];
|
||||
Array props = p_arr[2];
|
||||
|
||||
for (int i = 0; i < props.size(); i++) {
|
||||
|
||||
CHECK_TYPE(props[i], ARRAY);
|
||||
Array prop = props[i];
|
||||
|
||||
ERR_FAIL_COND(prop.size() != 6);
|
||||
CHECK_TYPE(prop[0], STRING);
|
||||
CHECK_TYPE(prop[1], INT);
|
||||
CHECK_TYPE(prop[2], INT);
|
||||
CHECK_TYPE(prop[3], STRING);
|
||||
CHECK_TYPE(prop[4], INT);
|
||||
|
||||
PropertyInfo pinfo;
|
||||
pinfo.name = prop[0];
|
||||
pinfo.type = Variant::Type(int(prop[1]));
|
||||
pinfo.hint = PropertyHint(int(prop[2]));
|
||||
pinfo.hint_string = prop[3];
|
||||
pinfo.usage = PropertyUsageFlags(int(prop[4]));
|
||||
Variant var = prop[5];
|
||||
|
||||
if (pinfo.type == Variant::OBJECT) {
|
||||
if (var.is_zero()) {
|
||||
var = RES();
|
||||
} else if (var.get_type() == Variant::OBJECT) {
|
||||
if (((Object *)var)->is_class("EncodedObjectAsID")) {
|
||||
var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id();
|
||||
pinfo.type = var.get_type();
|
||||
pinfo.hint = PROPERTY_HINT_OBJECT_ID;
|
||||
pinfo.hint_string = "Object";
|
||||
}
|
||||
}
|
||||
}
|
||||
properties.push_back(SceneDebuggerProperty(pinfo, var));
|
||||
}
|
||||
}
|
||||
|
||||
/// SceneDebuggerTree
|
||||
SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {
|
||||
// Flatten tree into list, depth first, use stack to avoid recursion.
|
||||
List<Node *> stack;
|
||||
stack.push_back(p_root);
|
||||
while (stack.size()) {
|
||||
Node *n = stack[0];
|
||||
stack.pop_front();
|
||||
int count = n->get_child_count();
|
||||
nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id()));
|
||||
for (int i = 0; i < count; i++) {
|
||||
stack.push_front(n->get_child(count - i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneDebuggerTree::serialize(Array &p_arr) {
|
||||
for (List<RemoteNode>::Element *E = nodes.front(); E; E = E->next()) {
|
||||
RemoteNode &n = E->get();
|
||||
p_arr.push_back(n.child_count);
|
||||
p_arr.push_back(n.name);
|
||||
p_arr.push_back(n.type_name);
|
||||
p_arr.push_back(n.id);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneDebuggerTree::deserialize(const Array &p_arr) {
|
||||
int idx = 0;
|
||||
while (p_arr.size() > idx) {
|
||||
ERR_FAIL_COND(p_arr.size() < 4);
|
||||
CHECK_TYPE(p_arr[idx], INT);
|
||||
CHECK_TYPE(p_arr[idx + 1], STRING);
|
||||
CHECK_TYPE(p_arr[idx + 2], STRING);
|
||||
CHECK_TYPE(p_arr[idx + 3], INT);
|
||||
nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3]));
|
||||
idx += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/// LiveEditor
|
||||
LiveEditor *LiveEditor::singleton = NULL;
|
||||
LiveEditor *LiveEditor::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
void LiveEditor::_send_tree() {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Array arr;
|
||||
// Encoded as a flat list depth fist.
|
||||
SceneDebuggerTree tree(scene_tree->root);
|
||||
tree.serialize(arr);
|
||||
ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
|
||||
}
|
||||
|
||||
void LiveEditor::_node_path_func(const NodePath &p_path, int p_id) {
|
||||
|
||||
live_edit_node_path_cache[p_id] = p_path;
|
||||
}
|
||||
|
||||
void LiveEditor::_res_path_func(const String &p_path, int p_id) {
|
||||
|
||||
live_edit_resource_cache[p_id] = p_path;
|
||||
}
|
||||
|
||||
void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
|
||||
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
if (!live_edit_node_path_cache.has(p_id))
|
||||
return;
|
||||
|
||||
NodePath np = live_edit_node_path_cache[p_id];
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(np))
|
||||
continue;
|
||||
Node *n2 = n->get_node(np);
|
||||
|
||||
n2->set(p_prop, p_value);
|
||||
}
|
||||
}
|
||||
|
||||
void LiveEditor::_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
|
||||
|
||||
RES r = ResourceLoader::load(p_value);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
_node_set_func(p_id, p_prop, r);
|
||||
}
|
||||
void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
if (!live_edit_node_path_cache.has(p_id))
|
||||
return;
|
||||
|
||||
NodePath np = live_edit_node_path_cache[p_id];
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(np))
|
||||
continue;
|
||||
Node *n2 = n->get_node(np);
|
||||
|
||||
n2->call(p_method, VARIANT_ARG_PASS);
|
||||
}
|
||||
}
|
||||
void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
|
||||
|
||||
if (!live_edit_resource_cache.has(p_id))
|
||||
return;
|
||||
|
||||
String resp = live_edit_resource_cache[p_id];
|
||||
|
||||
if (!ResourceCache::has(resp))
|
||||
return;
|
||||
|
||||
RES r = ResourceCache::get(resp);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
|
||||
r->set(p_prop, p_value);
|
||||
}
|
||||
void LiveEditor::_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
|
||||
|
||||
RES r = ResourceLoader::load(p_value);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
_res_set_func(p_id, p_prop, r);
|
||||
}
|
||||
void LiveEditor::_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
|
||||
|
||||
if (!live_edit_resource_cache.has(p_id))
|
||||
return;
|
||||
|
||||
String resp = live_edit_resource_cache[p_id];
|
||||
|
||||
if (!ResourceCache::has(resp))
|
||||
return;
|
||||
|
||||
RES r = ResourceCache::get(resp);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
|
||||
r->call(p_method, VARIANT_ARG_PASS);
|
||||
}
|
||||
|
||||
void LiveEditor::_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
|
||||
|
||||
live_edit_root = p_scene_path;
|
||||
live_edit_scene = p_scene_from;
|
||||
}
|
||||
|
||||
void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_parent))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_parent);
|
||||
|
||||
Node *no = Object::cast_to<Node>(ClassDB::instance(p_type));
|
||||
if (!no) {
|
||||
continue;
|
||||
}
|
||||
|
||||
no->set_name(p_name);
|
||||
n2->add_child(no);
|
||||
}
|
||||
}
|
||||
void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Ref<PackedScene> ps = ResourceLoader::load(p_path);
|
||||
|
||||
if (!ps.is_valid())
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_parent))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_parent);
|
||||
|
||||
Node *no = ps->instance();
|
||||
if (!no) {
|
||||
continue;
|
||||
}
|
||||
|
||||
no->set_name(p_name);
|
||||
n2->add_child(no);
|
||||
}
|
||||
}
|
||||
void LiveEditor::_remove_node_func(const NodePath &p_at) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F;) {
|
||||
|
||||
Set<Node *>::Element *N = F->next();
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
memdelete(n2);
|
||||
|
||||
F = N;
|
||||
}
|
||||
}
|
||||
void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F;) {
|
||||
|
||||
Set<Node *>::Element *N = F->next();
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
n2->get_parent()->remove_child(n2);
|
||||
|
||||
live_edit_remove_list[n][p_keep_id] = n2;
|
||||
|
||||
F = N;
|
||||
}
|
||||
}
|
||||
void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F;) {
|
||||
|
||||
Set<Node *>::Element *N = F->next();
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
Map<Node *, Map<ObjectID, Node *> >::Element *EN = live_edit_remove_list.find(n);
|
||||
|
||||
if (!EN)
|
||||
continue;
|
||||
|
||||
Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
|
||||
|
||||
if (!FN)
|
||||
continue;
|
||||
n2->add_child(FN->get());
|
||||
|
||||
EN->get().erase(FN);
|
||||
|
||||
if (EN->get().size() == 0) {
|
||||
live_edit_remove_list.erase(EN);
|
||||
}
|
||||
|
||||
F = N;
|
||||
}
|
||||
}
|
||||
void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_name) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS);
|
||||
|
||||
if (!dup)
|
||||
continue;
|
||||
|
||||
dup->set_name(p_new_name);
|
||||
n2->get_parent()->add_child(dup);
|
||||
}
|
||||
}
|
||||
void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (scene_tree->root->has_node(live_edit_root))
|
||||
base = scene_tree->root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *nfrom = n->get_node(p_at);
|
||||
|
||||
if (!n->has_node(p_new_place))
|
||||
continue;
|
||||
Node *nto = n->get_node(p_new_place);
|
||||
|
||||
nfrom->get_parent()->remove_child(nfrom);
|
||||
nfrom->set_name(p_new_name);
|
||||
|
||||
nto->add_child(nfrom);
|
||||
if (p_at_pos >= 0)
|
||||
nto->move_child(nfrom, p_at_pos);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
151
scene/debugger/scene_debugger.h
Normal file
151
scene/debugger/scene_debugger.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*************************************************************************/
|
||||
/* scene_debugger.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 SCENE_DEBUGGER_H
|
||||
#define SCENE_DEBUGGER_H
|
||||
|
||||
#include "core/array.h"
|
||||
#include "core/object.h"
|
||||
#include "core/pair.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
class SceneDebugger {
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static void deinitialize();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
private:
|
||||
static void _save_node(ObjectID id, const String &p_path);
|
||||
static void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
|
||||
static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20);
|
||||
|
||||
public:
|
||||
static Error parse_message(const String &p_msg, const Array &p_args);
|
||||
static void add_to_cache(const String &p_filename, Node *p_node);
|
||||
static void remove_from_cache(const String &p_filename, Node *p_node);
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
class SceneDebuggerObject {
|
||||
|
||||
private:
|
||||
void _parse_script_properties(Script *p_script, ScriptInstance *p_instance);
|
||||
|
||||
public:
|
||||
typedef Pair<PropertyInfo, Variant> SceneDebuggerProperty;
|
||||
ObjectID id;
|
||||
String class_name;
|
||||
List<SceneDebuggerProperty> properties;
|
||||
|
||||
SceneDebuggerObject(ObjectID p_id);
|
||||
SceneDebuggerObject() {}
|
||||
|
||||
void serialize(Array &r_arr, int p_max_size = 1 << 20);
|
||||
void deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class SceneDebuggerTree {
|
||||
|
||||
public:
|
||||
struct RemoteNode {
|
||||
int child_count;
|
||||
String name;
|
||||
String type_name;
|
||||
ObjectID id;
|
||||
|
||||
RemoteNode(int p_child, const String &p_name, const String &p_type, ObjectID p_id) {
|
||||
child_count = p_child;
|
||||
name = p_name;
|
||||
type_name = p_type;
|
||||
id = p_id;
|
||||
}
|
||||
|
||||
RemoteNode() {}
|
||||
};
|
||||
|
||||
List<RemoteNode> nodes;
|
||||
|
||||
void serialize(Array &r_arr);
|
||||
void deserialize(const Array &p_arr);
|
||||
SceneDebuggerTree(Node *p_root);
|
||||
SceneDebuggerTree(){};
|
||||
};
|
||||
|
||||
class LiveEditor {
|
||||
|
||||
private:
|
||||
friend class SceneDebugger;
|
||||
Map<int, NodePath> live_edit_node_path_cache;
|
||||
Map<int, String> live_edit_resource_cache;
|
||||
|
||||
NodePath live_edit_root;
|
||||
String live_edit_scene;
|
||||
|
||||
Map<String, Set<Node *> > live_scene_edit_cache;
|
||||
Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
|
||||
|
||||
void _send_tree();
|
||||
|
||||
void _node_path_func(const NodePath &p_path, int p_id);
|
||||
void _res_path_func(const String &p_path, int p_id);
|
||||
|
||||
void _node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
|
||||
void _node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
|
||||
void _node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
|
||||
void _res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
|
||||
void _res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
|
||||
void _res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
|
||||
void _root_func(const NodePath &p_scene_path, const String &p_scene_from);
|
||||
|
||||
void _create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
|
||||
void _instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name);
|
||||
void _remove_node_func(const NodePath &p_at);
|
||||
void _remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id);
|
||||
void _restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos);
|
||||
void _duplicate_node_func(const NodePath &p_at, const String &p_new_name);
|
||||
void _reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
|
||||
|
||||
LiveEditor() {
|
||||
singleton = this;
|
||||
live_edit_root = NodePath("/root");
|
||||
};
|
||||
|
||||
static LiveEditor *singleton;
|
||||
|
||||
public:
|
||||
static LiveEditor *get_singleton();
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -37,6 +37,7 @@
|
|||
#include "core/message_queue.h"
|
||||
#include "core/print_string.h"
|
||||
#include "instance_placeholder.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "viewport.h"
|
||||
|
@ -244,11 +245,7 @@ void Node::_propagate_enter_tree() {
|
|||
data.blocked--;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
if (ScriptDebugger::get_singleton() && data.filename != String()) {
|
||||
//used for live edit
|
||||
data.tree->live_scene_edit_cache[data.filename].insert(this);
|
||||
}
|
||||
SceneDebugger::add_to_cache(data.filename, this);
|
||||
#endif
|
||||
// enter groups
|
||||
}
|
||||
|
@ -268,26 +265,7 @@ void Node::_propagate_exit_tree() {
|
|||
//block while removing children
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
if (ScriptDebugger::get_singleton() && data.filename != String()) {
|
||||
//used for live edit
|
||||
Map<String, Set<Node *> >::Element *E = data.tree->live_scene_edit_cache.find(data.filename);
|
||||
if (E) {
|
||||
E->get().erase(this);
|
||||
if (E->get().size() == 0) {
|
||||
data.tree->live_scene_edit_cache.erase(E);
|
||||
}
|
||||
}
|
||||
|
||||
Map<Node *, Map<ObjectID, Node *> >::Element *F = data.tree->live_edit_remove_list.find(this);
|
||||
if (F) {
|
||||
for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
|
||||
|
||||
memdelete(G->get());
|
||||
}
|
||||
data.tree->live_edit_remove_list.erase(F);
|
||||
}
|
||||
}
|
||||
SceneDebugger::remove_from_cache(data.filename, this);
|
||||
#endif
|
||||
data.blocked++;
|
||||
|
||||
|
|
|
@ -38,9 +38,10 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/print_string.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "main/input_default.h"
|
||||
#include "node.h"
|
||||
#include "scene/debugger/script_debugger_remote.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/resources/dynamic_font.h"
|
||||
#include "scene/resources/material.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
|
@ -1329,380 +1330,6 @@ void SceneTree::add_current_scene(Node *p_current) {
|
|||
root->add_child(p_current);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
static void _fill_array(Node *p_node, Array &array, int p_level) {
|
||||
|
||||
array.push_back(p_node->get_child_count());
|
||||
array.push_back(p_node->get_name());
|
||||
array.push_back(p_node->get_class());
|
||||
array.push_back(p_node->get_instance_id());
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
|
||||
_fill_array(p_node->get_child(i), array, p_level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTree::_debugger_request_tree() {
|
||||
|
||||
Array arr;
|
||||
_fill_array(root, arr, 0);
|
||||
ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
|
||||
}
|
||||
|
||||
void SceneTree::_live_edit_node_path_func(const NodePath &p_path, int p_id) {
|
||||
|
||||
live_edit_node_path_cache[p_id] = p_path;
|
||||
}
|
||||
|
||||
void SceneTree::_live_edit_res_path_func(const String &p_path, int p_id) {
|
||||
|
||||
live_edit_resource_cache[p_id] = p_path;
|
||||
}
|
||||
|
||||
void SceneTree::_live_edit_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
|
||||
|
||||
if (!live_edit_node_path_cache.has(p_id))
|
||||
return;
|
||||
|
||||
NodePath np = live_edit_node_path_cache[p_id];
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(np))
|
||||
continue;
|
||||
Node *n2 = n->get_node(np);
|
||||
|
||||
n2->set(p_prop, p_value);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTree::_live_edit_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
|
||||
|
||||
RES r = ResourceLoader::load(p_value);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
_live_edit_node_set_func(p_id, p_prop, r);
|
||||
}
|
||||
void SceneTree::_live_edit_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
|
||||
|
||||
if (!live_edit_node_path_cache.has(p_id))
|
||||
return;
|
||||
|
||||
NodePath np = live_edit_node_path_cache[p_id];
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(np))
|
||||
continue;
|
||||
Node *n2 = n->get_node(np);
|
||||
|
||||
n2->call(p_method, VARIANT_ARG_PASS);
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
|
||||
|
||||
if (!live_edit_resource_cache.has(p_id))
|
||||
return;
|
||||
|
||||
String resp = live_edit_resource_cache[p_id];
|
||||
|
||||
if (!ResourceCache::has(resp))
|
||||
return;
|
||||
|
||||
RES r = ResourceCache::get(resp);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
|
||||
r->set(p_prop, p_value);
|
||||
}
|
||||
void SceneTree::_live_edit_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
|
||||
|
||||
RES r = ResourceLoader::load(p_value);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
_live_edit_res_set_func(p_id, p_prop, r);
|
||||
}
|
||||
void SceneTree::_live_edit_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
|
||||
|
||||
if (!live_edit_resource_cache.has(p_id))
|
||||
return;
|
||||
|
||||
String resp = live_edit_resource_cache[p_id];
|
||||
|
||||
if (!ResourceCache::has(resp))
|
||||
return;
|
||||
|
||||
RES r = ResourceCache::get(resp);
|
||||
if (!r.is_valid())
|
||||
return;
|
||||
|
||||
r->call(p_method, VARIANT_ARG_PASS);
|
||||
}
|
||||
|
||||
void SceneTree::_live_edit_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
|
||||
|
||||
live_edit_root = p_scene_path;
|
||||
live_edit_scene = p_scene_from;
|
||||
}
|
||||
|
||||
void SceneTree::_live_edit_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name) {
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_parent))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_parent);
|
||||
|
||||
Node *no = Object::cast_to<Node>(ClassDB::instance(p_type));
|
||||
if (!no) {
|
||||
continue;
|
||||
}
|
||||
|
||||
no->set_name(p_name);
|
||||
n2->add_child(no);
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name) {
|
||||
|
||||
Ref<PackedScene> ps = ResourceLoader::load(p_path);
|
||||
|
||||
if (!ps.is_valid())
|
||||
return;
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_parent))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_parent);
|
||||
|
||||
Node *no = ps->instance();
|
||||
if (!no) {
|
||||
continue;
|
||||
}
|
||||
|
||||
no->set_name(p_name);
|
||||
n2->add_child(no);
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_remove_node_func(const NodePath &p_at) {
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F;) {
|
||||
|
||||
Set<Node *>::Element *N = F->next();
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
memdelete(n2);
|
||||
|
||||
F = N;
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id) {
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F;) {
|
||||
|
||||
Set<Node *>::Element *N = F->next();
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
n2->get_parent()->remove_child(n2);
|
||||
|
||||
live_edit_remove_list[n][p_keep_id] = n2;
|
||||
|
||||
F = N;
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F;) {
|
||||
|
||||
Set<Node *>::Element *N = F->next();
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
Map<Node *, Map<ObjectID, Node *> >::Element *EN = live_edit_remove_list.find(n);
|
||||
|
||||
if (!EN)
|
||||
continue;
|
||||
|
||||
Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
|
||||
|
||||
if (!FN)
|
||||
continue;
|
||||
n2->add_child(FN->get());
|
||||
|
||||
EN->get().erase(FN);
|
||||
|
||||
if (EN->get().size() == 0) {
|
||||
live_edit_remove_list.erase(EN);
|
||||
}
|
||||
|
||||
F = N;
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name) {
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *n2 = n->get_node(p_at);
|
||||
|
||||
Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS);
|
||||
|
||||
if (!dup)
|
||||
continue;
|
||||
|
||||
dup->set_name(p_new_name);
|
||||
n2->get_parent()->add_child(dup);
|
||||
}
|
||||
}
|
||||
void SceneTree::_live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
|
||||
|
||||
Node *base = NULL;
|
||||
if (root->has_node(live_edit_root))
|
||||
base = root->get_node(live_edit_root);
|
||||
|
||||
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
|
||||
if (!E)
|
||||
return; //scene not editable
|
||||
|
||||
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
|
||||
|
||||
Node *n = F->get();
|
||||
|
||||
if (base && !base->is_a_parent_of(n))
|
||||
continue;
|
||||
|
||||
if (!n->has_node(p_at))
|
||||
continue;
|
||||
Node *nfrom = n->get_node(p_at);
|
||||
|
||||
if (!n->has_node(p_new_place))
|
||||
continue;
|
||||
Node *nto = n->get_node(p_new_place);
|
||||
|
||||
nfrom->get_parent()->remove_child(nfrom);
|
||||
nfrom->set_name(p_new_name);
|
||||
|
||||
nto->add_child(nfrom);
|
||||
if (p_at_pos >= 0)
|
||||
nto->move_child(nfrom, p_at_pos);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void SceneTree::drop_files(const Vector<String> &p_files, int p_from_screen) {
|
||||
|
||||
emit_signal("files_dropped", p_files, p_from_screen);
|
||||
|
@ -2116,11 +1743,6 @@ SceneTree::SceneTree() {
|
|||
_update_root_rect();
|
||||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (ScriptDebugger::get_singleton()->is_remote()) {
|
||||
ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(ScriptDebugger::get_singleton());
|
||||
|
||||
remote_debugger->set_scene_tree(this);
|
||||
}
|
||||
ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
|
||||
}
|
||||
|
||||
|
@ -2129,12 +1751,6 @@ SceneTree::SceneTree() {
|
|||
#ifdef TOOLS_ENABLED
|
||||
edited_scene_root = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
live_edit_root = NodePath("/root");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
SceneTree::~SceneTree() {
|
||||
|
|
|
@ -44,6 +44,7 @@ class Node;
|
|||
class Viewport;
|
||||
class Material;
|
||||
class Mesh;
|
||||
class SceneDebugger;
|
||||
|
||||
class SceneTreeTimer : public Reference {
|
||||
GDCLASS(SceneTreeTimer, Reference);
|
||||
|
@ -219,39 +220,8 @@ private:
|
|||
|
||||
SelfList<Node>::List xform_change_list;
|
||||
|
||||
friend class ScriptDebuggerRemote;
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
Map<int, NodePath> live_edit_node_path_cache;
|
||||
Map<int, String> live_edit_resource_cache;
|
||||
|
||||
NodePath live_edit_root;
|
||||
String live_edit_scene;
|
||||
|
||||
Map<String, Set<Node *> > live_scene_edit_cache;
|
||||
Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
|
||||
|
||||
void _debugger_request_tree();
|
||||
|
||||
void _live_edit_node_path_func(const NodePath &p_path, int p_id);
|
||||
void _live_edit_res_path_func(const String &p_path, int p_id);
|
||||
|
||||
void _live_edit_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
|
||||
void _live_edit_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
|
||||
void _live_edit_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
|
||||
void _live_edit_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
|
||||
void _live_edit_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
|
||||
void _live_edit_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
|
||||
void _live_edit_root_func(const NodePath &p_scene_path, const String &p_scene_from);
|
||||
|
||||
void _live_edit_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
|
||||
void _live_edit_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name);
|
||||
void _live_edit_remove_node_func(const NodePath &p_at);
|
||||
void _live_edit_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id);
|
||||
void _live_edit_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos);
|
||||
void _live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name);
|
||||
void _live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
|
||||
|
||||
#ifdef DEBUG_ENABLED // No live editor in release build.
|
||||
friend class LiveEditor;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#include "scene/animation/root_motion_view.h"
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/audio/audio_stream_player.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/center_container.h"
|
||||
|
@ -777,10 +778,12 @@ void register_scene_types() {
|
|||
ERR_PRINT("Error loading custom theme '" + theme_path + "'");
|
||||
}
|
||||
}
|
||||
SceneDebugger::initialize();
|
||||
}
|
||||
|
||||
void unregister_scene_types() {
|
||||
|
||||
SceneDebugger::deinitialize();
|
||||
clear_default_theme();
|
||||
|
||||
ResourceLoader::remove_resource_format_loader(resource_loader_dynamic_font);
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include "audio_server.h"
|
||||
#include "camera/camera_feed.h"
|
||||
#include "camera_server.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "navigation_2d_server.h"
|
||||
#include "navigation_server.h"
|
||||
#include "physics/physics_server_sw.h"
|
||||
|
@ -63,18 +64,17 @@
|
|||
#include "physics_2d/physics_2d_server_wrap_mt.h"
|
||||
#include "physics_2d_server.h"
|
||||
#include "physics_server.h"
|
||||
#include "scene/debugger/script_debugger_remote.h"
|
||||
#include "visual/shader_types.h"
|
||||
#include "visual_server.h"
|
||||
|
||||
static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsage> *r_usage) {
|
||||
static void _debugger_get_resource_usage(ScriptDebuggerRemote::ResourceUsage *r_usage) {
|
||||
|
||||
List<VS::TextureInfo> tinfo;
|
||||
VS::get_singleton()->texture_debug_usage(&tinfo);
|
||||
|
||||
for (List<VS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
|
||||
|
||||
ScriptDebuggerRemote::ResourceUsage usage;
|
||||
ScriptDebuggerRemote::ResourceInfo usage;
|
||||
usage.path = E->get().path;
|
||||
usage.vram = E->get().bytes;
|
||||
usage.id = E->get().texture;
|
||||
|
@ -84,7 +84,7 @@ static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsag
|
|||
} else {
|
||||
usage.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
|
||||
}
|
||||
r_usage->push_back(usage);
|
||||
r_usage->infos.push_back(usage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue